Reputation: 1011
I want something like this:
type Person = { age:number, weight: number}
const people:{[key:infer_the_type]:Person} = {
mike: {...},
bob: {...},
}
so that if I do this, it works:
keyof typeof people // This should be "mike" | "bob"
Is this possible?
Upvotes: 3
Views: 487
Reputation: 328067
You cannot get the behavior you're looking for by annotating the type of people
; once you annotate people
with a (non-union) type, the compiler will automatically throw away any more specific information it had about the initialized value. There's a (longstanding) open issue in GitHub, microsoft/TypeScript#7481 which asks for something like your infer_the_type
(especially relevant is microsoft/TypeScript#38349 which was closed as a duplicate). If you want to see that happen, you might want to go to that issue and give it a 👍, but it's not clear when or if this will ever be implemented.
Instead, you can get similar behavior by replacing your type annotation with a generic helper function:
const asPeople =
<K extends PropertyKey>(people: { [P in K]: Person }) => people;
const people = asPeople({
mike: { name: "Mike", weight: 150 },
bob: { name: "Bob", weight: 200 },
}); // okay
You can verify that the compiler has inferred the keys of people
:
/* const people: {
mike: Person;
bob: Person;
} */
And the helper function will report an error if you pass it something unexpected:
const badPeople = asPeople({
alice: { name: "Alice", height: 75 } // error!
// -------------------> ~~~~~~~~~~
// Did you mean to write 'weight'?
})
Upvotes: 2
Reputation: 9354
I would be tempted to create a helper function to do this. It's a little bit ugly since it means having pointless functions floating around at runtime but will get the job done:
const getPeople = <P extends PropertyKey>(p: Record<P, Person>) => p;
const typedPeople = getPeople({
mike: {name: "mike", weight: 32},
bob: {name: "bob", weight: 6},
})
typedPeople
will be of type Record<'mike' | 'bob', Person>
.
Alternatively, since this is presumably a static list of names (I don't see how this would work otherwise), it might just be better to create the type beforehand, assming the list is not too long or subject to change:
type Names = 'mike' | 'bob';
Even more alternatively, I am a little suspicious of the need to explicitly type people
at all. As you've already demonstrated, you can use keyof
to get the names out of the object after instantiating it, so anywhere it's going to get used should have the types already available.
Upvotes: 1