Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 },
}
like image 697
Jack Wilsdon Avatar asked Oct 30 '25 05:10

Jack Wilsdon


1 Answers

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 },
}
like image 131
Titian Cernicova-Dragomir Avatar answered Nov 01 '25 20:11

Titian Cernicova-Dragomir



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!