Reputation: 138
I want to define the type of a property based on the generic type of its sibling property.
for example, assume we have:
type Person = {
id: number;
name: string;
}
type Select<Value=unknown> = (props:Props<Value>) => any;
const PersonSelect:Select<Person> = (props) => null //implementation is irrelavant
const TextSelect:Select<string> = (props) => null //implementation is irrelavant
then I want to be able to do something like this:
type Filter<V = unknown > = {
component: Select<V>,
transformer: (value: V) => string
};
const filters:Array<Filter> = [
{
component: PersonSelect,
transformer: (selected) => selected.name //type of `selected` should be infered as `Person`
},
{
component: TextSelect,
transformer: (selected) => selected // type of `selected` should be infered as `string`
}
]
Workaround
With the above code I can do it like
const personFilter:Filter<Person> = {
component: PersonSelect,
transformer: (selected) => selected.name
}
const textFilter:Filter<string> = {
component: TextSelect,
transformer: (selected) => selected
}
const filters = [personFilter, textFilter];
But I'm looking for a solution that works without explicitly defining the type of each filter object. Also, the generic V
type can be anything, so I can't use the union of all possible combinations.
Is there a better solution?
Upvotes: 3
Views: 1151
Reputation: 33061
Seems to be TypeScript having hard time to infer arrow function property argument based on other property of same object.
I was unable to do it with help of function inference.
The most simplest way is to make builder
function which returns Filter<_>
.
type Person = {
id: number;
name: string;
}
type Props<T> = T
type Select<Value = unknown> = (props: Props<Value>) => any;
const PersonSelect: Select<Person> = (props) => null //implementation is irrelavant
const TextSelect: Select<string> = (props) => null //implementation is irrelavant
type Filter<V = unknown> = {
component: Select<V>,
transformer: <T extends V>(value: T & V) => string
};
const builder = <V,>(
component: Select<V>,
transformer: (value: V) => string
): Filter<V> =>
({ component, transformer })
const filters = [
builder(PersonSelect, (selected) => selected.name),
builder(TextSelect, (selected) => selected)
]
Because component
and transformer
arguments are not the part of same data structure, it is easier to infer them and apply our type constraints.
Here, you can find more examples of type Inference on function arguments.
Upvotes: 2