Reputation: 953
How can I get Typescript to understand what the value type of an object is without losing access to the key information from its static declaration?
In the below code I want the benefit of
This object is homogenous and will have lots of functions, specifying the signature every time seems like a pain.
Example 1 - TS doesn't know the keys of the object
interface Foo {
[key: string]: (n: number) => any
}
const obj1: Foo = {
// TS already knows that `n` is a number 🙂
bar(n) {return n}
}
type KeyOfObj1 = keyof typeof obj1
const kob1: KeyOfObj1 = "xyz" // This does not fail static type checking 😢
Example 2 - TS doesn't know the signature of the functions which are the object's values
const obj2 = {
// Notice here I have to specify the type of `n` 😢
bar(n: number) {return n}
}
type KeyOfObj2 = keyof typeof obj2
const kob2: KeyOfObj2 = "xyz" // This fails static type checking 🙂
How can I get the best of both worlds?
p.s. I understand I could pull out the keys into an enum but that seems rather ugly and repetitive, though perhaps it really is the only way 😕
Upvotes: 3
Views: 163
Reputation: 51132
This is a fairly common situation: you want to write an object, have its members contextually typed according to some weaker type (Foo
in this case), but still retain the stronger type for some other purpose (in this case, you don't want to "forget" what the keys are).
The usual thing to do is to write a generic identity function, like below, which checks that the argument type T
is a subtype of Foo
, while returning the stricter type T
:
function foo<T extends Foo>(obj: T): T {
return obj;
}
Then when you create the object, the compiler will contextually type it as if it is some subtype of Foo
, but still keep that subtype (with its specific key names) for the assignment to obj
:
const obj = foo({
foo(n) { return n + 1; },
bar(n) { return n * 2; },
});
// 'foo' | 'bar'
type ObjKeys = keyof typeof obj
Unfortunately, at least right now, you do need the generic identity function for this to work. There is currently a proposal to add new syntax to the language which would allow this kind of problem to be solved without adding a "useless" extra function.
Upvotes: 4