Answer:
We have a discriminated union which accepts an argument of itself:
type TextInput<Props extends Inputs<Props>> = {
type: 'text',
value?: string,
options?: undefined
}
type CheckboxInput<Props extends Inputs<Props>> = {
type: 'checkbox',
options: string[] | number[],
value?: Props['options'] extends Array<infer T> ? T : never
}
type Events<Props extends Inputs<Props>> = { onInput?: (arg: Inputs<Props>['value']) => any }
type Inputs<Props extends Inputs<Props>> = TextInput<Props> | CheckboxInput<Props>
This works, and we can do some remarkable things, such as passing a string
for a TextInput
parameter value
, and passing a number
matching the type of options
for a CheckboxInput
parameter value
.
We can use the union as an argument to a function like so:
type Make = <P extends Inputs<P>>(props: P) => {}
declare const create: Make
create({ type: 'text', value: '123' }) // π
create({ type: 'text', value: 123 }) // π (value must be a string)
create({ type: 'checkbox', options: ['a', 'b'], value: 'a' }) // π
create({ type: 'checkbox', options: [1, 2, 3], value: 2 }) // π
create({ type: 'checkbox', options: ['a', 'b'], value: 2 }) // π (value must match options type)
However, is there any way to use the narrowed union data as the argument of a method inside the union? For example, if we add a onInput
method to the union, can we ensure that the argument of that method also matches the value type?
type TextInput<Props extends Inputs<Props>> = {
type: 'text',
value?: string,
options?: undefined
} & Events<Props>
type CheckboxInput<Props extends Inputs<Props>> = {
type: 'checkbox',
options: string[] | number[],
value?: Props['options'] extends Array<infer T> ? T : never
} & Events<Props>
type Events<Props extends Inputs<Props>> = { onInput?: (arg: Inputs<Props>['value']) => any }
type Inputs<Props extends Inputs<Props>> = TextInput<Props> | CheckboxInput<Props>
type Make = <P extends Inputs<P>>(props: P) => {}
declare const create: Make
create({ type: 'checkbox', options: [1, 2, 3], value: 1, onInput: (arg) => arg }) // π "arg" in onInput should only be a number, matching the value type
Question: Is there any way to use the narrowed union data as the argument of a method inside the union?