Reputation: 3387
Given I have a type like:
type Foo = {
foo: number;
bar: string;
baz: boolean;
}
I want to have a type Buzz
which can detect the value type against a key, i.e
const abc: Buzz<Foo> = {
key: 'foo',
formatter: (detectMe) => {} //I want TS to infer this to be 'number'
};
Given the key 'foo' the argument in formatter should be inferred to be number
. I tried this:
interface ColumnDescription<T> {
label: string;
key: keyof T;
formatter?: (datum: T[keyof T]) => void;
}
However this results in the argument being inferred to be number | string | boolean
.
Tried this as well:
interface ColumnDescription<T, K extends keyof T> {
label: string;
key: K;
formatter?: (datum: T[K]) => void;
}
This sort of works but I always need the specify the key in the second type argument, instead of it happening automatically. i.e:
const abc: Buzz<Foo, 'foo'> = { //I don't want to specify the key
key: 'foo',
formatter: (detectMe) => {} //This is inferred correctly
};
Upvotes: 3
Views: 1874
Reputation: 328272
As in my comment, I'd suggest
type Buzz<T> = {
[K in keyof T]-?: { key: K; formatter: (d: T[K]) => void }
}[keyof T];
which is similar to what you did with Buzz<T, K extends keyof T>
, but instead of making Buzz
need K
to be specified, I used a mapped type {[K in keyof T]: ...}
which automatically iterates over keys in keyof T
and makes a new object with the same keys but whose property values are the types you're looking for. That means to get the desired Buzz<T>
we need to look up the property values, by indexing into it with [keyof T]
. That makes Buzz<T>
a union of types, where each constituent of the union corresponds to your Buzz<T, K extends keyof T>
for a particular key K
Let's make sure it works:
const abc: Buzz<Foo> = {
key: "foo",
formatter: detectMe => {} // inferred as number, as desired
};
Looks good, and let's inspect the type of abc
with IntelliSense:
const abc: {
key: "foo";
formatter: (d: number) => void;
} | {
key: "bar";
formatter: (d: string) => void;
} | {
key: "baz";
formatter: (d: boolean) => void;
}
That looks good too.
Okay, hope that helps; good luck!
Upvotes: 5