Gbox4
Gbox4

Reputation: 633

How do I make a function accept only keys that map to a certain type?

How do I make a function accept only keys that map to certain type?

type Foo = {
    // Arbitrary, can be any type
    foo: number;
    bar: number;
    baz: string;
}

function f<T>(obj: T, key: keyof T) {
    const foo = obj[key] // foo should be type number
}

const x: Foo = {foo: 0, bar: 1, baz: "a"}
f(x, "foo") // OK
f(x, "bar") // OK
f(x, "baz") // Should give error, because x["baz"] is not of type number

I have tried extending T, but can't get it to use the key from the argument:

function f<T extends {key: number}>(obj: T, key: keyof T) {} // Doesn't work

I have also tried extracting all fields from Foo which are numbers, and taking the keyof that, but Extract doesn't seem to work in quite that way:

function f<T>(obj: T, key: keyof (Extract<T, number>))) {} // Also doesn't work

Is this possible in TypeScript?

Link to playground

Upvotes: 2

Views: 73

Answers (1)

chris
chris

Reputation: 2621

Interesting question! Managed to solve that puzzle. Yes, it can be done with the help of conditional types, but brace yourself, it's quite convoluted:

type NumberProp<T> = keyof T extends infer K 
    ? K extends keyof T 
        ? T[K] extends number 
            ? K : never : never : never;

function f<T>(obj: T, key: NumberProp<T>) {
    const foo = obj[key];
}

const x = {foo: 0, bar: 1, baz: "a"};
f(x, "foo"); // OK
f(x, "bar"); // OK
f(x, "baz"); // ERROR

Upvotes: 2

Related Questions