TypeScript: 为什么我的默认泛型类型导致参数只能是该类型的确定性?

huangapple go评论62阅读模式
英文:

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?

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

答案1

得分: 1

您的界面是通用的,但您的函数组件不是,因此通用类型默认为它们的默认类型。因此,您需要使函数成为通用的。

但现在您需要处理preSelected是数组的情况,这仍然会导致错误。

英文:

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.

答案2

得分: 0

看起来问题在于preSelected类型没有被正确地推断。

您可以通过在函数签名中为preSelected提供显式类型或使用multiple属性的类型推断来修复这个问题:

export const MyComponent = (prop: MyComponentProp) => {
  const {
    optionLabel,
    optionValue,
    multiple,
    preSelected,
  } = prop

  const preSelectedType = multiple ? 
    (prop.preSelected as MyComponentOption[])[0].label ? HasLabelValue : HasNameId :
    (prop.optionLabel === 'label' ? HasLabelValue : HasNameId);

  if (preSelected == null) console.log('没有预选选项')
  else {
    if (isOfLabelValue(preSelected as preSelectedType)) {
      console.log((preSelected as preSelectedType).label)
    } else {
      console.log((preSelected as preSelectedType).name)
    }
  }
}
英文:

It seems that the problem is that the preSelected type is not being inferred correctly.

You can fix this by providing an explicit type for preSelected in the function signature or using type inference based on the multiple property:

export const MyCOmponent = (prop: MyComponentProp) => {
const {
optionLabel,
optionValue,
multiple,
preSelected,
} = prop
const preSelectedType = multiple ? 
(prop.preSelected as MyComponentOption[])[0].label ? HasLabelValue : HasNameId :
(prop.optionLabel === 'label' ? HasLabelValue : HasNameId);
if (preSelected == null) console.log('no preselected options')
else {
if (isOfLabelValue(preSelected as preSelectedType)) {
console.log((preSelected as preSelectedType).label)
} else {
console.log((preSelected as preSelectedType).name)
}
}
}

huangapple
  • 本文由 发表于 2023年2月8日 19:00:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/75384810.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定