Skip to the content.

Associated types in TypeScript

In Swift, you can associate two types by declaring associatetype then defining it using typealias.

protocol Type {
    var name: String { get }
}

struct TypeX: Type {
    let name = "X"
}

struct TypeY: Type {
    let name = "Y"
}

protocol Value {
    associatedtype ValueType
    var type: ValueType { get }
}

struct ValueX: Value {
    typealias ValueType = TypeX
    let type = TypeX()
}

struct ValueY: Value {
    typealias ValueType = TypeY
    let type = TypeY()
}

protocol User {
    associatedtype V: Value
    var type: V.ValueType { get }
    var value: V { get }
}

class UserX: User {
    typealias V = ValueX
    var type: TypeX { return TypeX() }
    var value: ValueX { return ValueX() }
}

let x = UserX()
print(x.type.name)
print(x.value.type.name)

How can we do this in TypeScript? In TypeScript, you cannot put an associated type in an interface. Instead, you need to pass it as a generic parameter to an interface, then use a conditional type to retrieve it.

type Type = 'X' | 'Y'

interface Value<Type> {
    type: Type
}

class ValueX implements Value<'X'> {
    type: 'X' = 'X'
}

class ValueY implements Value<'Y'> {
    type: 'Y' = 'Y'
}

type ValueType<V> = V extends Value<infer T> ? T : never

interface User<V extends Value<any>> {
    type: ValueType<V>;
    value: V;
}

class UserX implements User<ValueX> {
    type: 'X' = 'X';
    value = new ValueX();
}

const x = new UserX();
console.log(x.type, x.value.type);

Here, ValueType is a conditional type and it’ll be inferred to a generic type passed to Value interface.