Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

typescript: Why does my default generic type results the determination that the param could be of that type only?

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?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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)
}


here’s a playground link

>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.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading