I tried to use a generic type with a default value
and use inference based on it to determine other function arguments,
but it seems to determine that the default type is the only type allowed for that argument,
is that supposed to happen?
am i doing something wrong?
this is the code snippet:
type MyComponentOption = HasNameId | HasLabelValue
type HasLabelValue = { label: string, value: number }
type HasNameId = { name: string, id: number }
interface MyComponentProp<
OptL extends ('label' | 'name') = 'label',
OptV = OptL extends 'label' ? 'value' : 'id',
Multiple extends boolean = false,
S = OptL extends 'label' ? HasLabelValue : HasNameId,
> {
optionLabel?: OptL;
optionValue?: OptV;
multiple?: Multiple,
preSelected?: Multiple extends true ? S[] : S;
}
export const MyCOmponent = (prop: MyComponentProp) => {
const {
optionLabel,
optionValue,
multiple,
preSelected,
} = prop
// v const preSelected: HasLabelValue | undefined
if (preSelected == null) console.log('no preselected options')
else {
if (isOfLabelValue(preSelected)) {
console.log(preSelected.label)
} else {
console.log(preSelected.name) // Error : Property 'name' does not exist on type 'never'
}
}
}
function isOfLabelValue(opt: MyComponentOption): opt is HasLabelValue {
return ('label' in opt && 'value' in opt)
}
>Solution :
Your interface is generic but your function component is not, so the generic types default to their default types. So, you need to make the function generic
type MyComponentOption = HasNameId | HasLabelValue
type HasLabelValue = { label: string, value: number }
type HasNameId = { name: string, id: number }
interface MyComponentProp<
OptL extends ('label' | 'name') = 'label',
OptV = OptL extends 'label' ? 'value' : 'id',
Multiple extends boolean = false,
// S needs to extend MyComponentOption, you assign default values but users of this interface can type anything in there if you don't extend
S extends MyComponentOption = OptL extends 'label' ? HasLabelValue : HasNameId,
> {
optionLabel?: OptL;
optionValue?: OptV;
multiple?: Multiple,
preSelected?: Multiple extends true ? S[] : S;
}
export const MyCOmponent = <
OptL extends ('label' | 'name') = 'label',
OptV = OptL extends 'label' ? 'value' : 'id',
Multiple extends boolean = false,
S extends MyComponentOption = OptL extends 'label' ? HasLabelValue : HasNameId,
>(prop: MyComponentProp<OptL, OptV, Multiple, S>) => {
const {
optionLabel,
optionValue,
multiple,
preSelected,
} = prop
// v const preSelected: HasLabelValue | undefined
if (preSelected == null) console.log('no preselected options')
else {
if (isOfLabelValue(preSelected)) {
console.log(preSelected.label)
} else {
console.log(preSelected.name)
}
}
}
function isOfLabelValue(opt: MyComponentOption | MyComponentOption[]): opt is HasLabelValue {
return ('label' in opt && 'value' in opt)
}
But now you need to handle cases where preSelected is an array, so this will still error.