Reputation: 7941
I have the following types:
type Filter = {
display: string
id: string
argType: 'string' | 'number' | 'date' | 'none'
}
type FilterMap = {[key: string]: Filter}
However, in this application, each string
key in a FilterMap
needs to match the id
attribute of the corresponding Filter
value.
So far, I've managed to get this to work:
type Filter<ID extends string> = {
display: string
id: ID
argType: 'string' | 'number' | 'date' | 'none'
}
type FilterMap<IDS extends string> = {[ID in IDS]: Filter<ID>}
let x: FilterMap<'foo' | 'bar'> = {
foo: {display: 'Foo', id: 'foo', argType: 'string'},
bar: {display: 'Bar', id: 'bar', argType: 'string'},
}
This will produce errors if, for example, the second-last line instead read bar: {display: 'Bar', id: 'baz', argType: 'string'},
, which is what I want!
Is there a way I can do this without having to type all of the keys out a third time, as I'm having to in the type argument to FilterMap
here?
Upvotes: 3
Views: 408
Reputation: 250326
You can do this if you take advantage of the inference behavior of functions. We can create a function with a generic parameter to represent the union of string literal types and let the compiler figure out what that type is, which it will do.
type Filter<ID extends string> = {
display: string
id: ID
argType: 'string' | 'number' | 'date' | 'none'
}
type FilterMap<IDS extends string> = { [ID in IDS]: Filter<ID> }
function createFilterMap<T extends string>(fm: FilterMap<T>) {
return fm
}
// Will be of type FilterMap<"foo" | "bar">
let x = createFilterMap({
foo: { display: 'Foo', id: 'foo', argType: 'string' },
bar: { display: 'Bar', id: 'bar', argType: 'string' },
})
Upvotes: 2