Jack Wilsdon
Jack Wilsdon

Reputation: 7025

TypeScript mapped type value depending on key

Can I constrain the value type of a map entry depending on the item's key?

type Thing<T extends string = any> = {
    type: T
}

type ThingMap<T extends Thing> = {
    [K in T["type"]]: T
}

interface A {
    type: "A",
    foo: boolean,
}

interface B {
    type: "B",
}


// This compiles.
const map: ThingMap<A | B> = {
    A: { type: "A", foo: true },
    B: { type: "B" },
}

// But this also compiles, when it should not.
const map: ThingMap<A | B> = {
    A: { type: "A", foo: true },
    B: { type: "A", foo: true },
}

Upvotes: 4

Views: 1222

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249466

You want to ensure that for a specific K the type of the key is not the whole union (ie T) but rather just the member of the union with the type K. You can use Extract to get the member of the union that has the property type of type K:

type Thing<T extends string = any> = {
    type: T
}

type ThingMap<T extends Thing> = {
    [K in T["type"]]:  Extract<T, { type : K }>  // Extract the union member that has { type: K }
}

interface A {
    type: "A",
    foo: boolean,
}

interface B {
    type: "B",
}


// This compiles.
const map: ThingMap<A | B> = {
    A: { type: "A", foo: true },
    B: { type: "B" },
}

// Err now
const map2: ThingMap<A | B> = {
    A: { type: "A", foo: true },
    B: { type: "A", foo: true },
}

Upvotes: 5

Related Questions