I have objects that implements types like this:
type TMyObject<T extends string> {
category: T
}
I need to statically store them in an other object, and ensure that the key of this second object matches the value of the category
field, like so:
const myObject: TMyObject<'foo'> = { category: 'foo' }
const dico = {
foo: myObject, // good
bar: myObject, // bad: 'bar' key does not match myObject.category
}
I encounter this case, because I have interfaces that extends IMyObject
and fixes the category field to a precise value, like so:
type TMyFooObject = IMyObject<'foo'>
I spent two hours trying to create a type for the dico
object that would work as described, but I just cannot figure a way to solve this ^^
Important note: The category
field and the possibles types extending TMyObject
are not static, we cannot use a "simple" union here...
As ever, a huge thanks for the time spent reading, and maybe answering to this question !
We can make use of mapped types to define the values of an object based on their key.
type Dico<Keys extends string> = {
[K in Keys]: TMyObject<K>
}
If we know all of the categories beforehand then we can create a map with those categories as keys.
const dico: Dico<'foo' | 'bar'> = {
foo: { category: 'foo' }, // good
bar: { category: 'foo' }, // error: Type '"foo"' is not assignable to type '"bar"'
}
You could use Partial<Dico<Category>>
if you want to limit it to a set of valid categories but not require all of them to be present.
This makes sense only if your types are setup such that you already have an easy way to access a union type of all of the categories.
We want to be able to just look at an object and see if its keys match its category strings. We can do that, but only if we create the object through an identity function. A generic function allows us to infer the Keys
type from the object.
const makeDico = <Keys extends string>(dico: Dico<Keys>) => dico;
const dico = makeDico({
foo: { category: 'foo' }, // good
bar: { category: 'foo' }, // error: Type '"foo"' is not assignable to type '"bar"'
});
// dico has type: Dico<"foo" | "bar">
Typescript Playground Link
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With