打字稿:如何正确使用泛型正确推断函数的返回类型?

时间:2019-10-31 12:47:28

标签: javascript typescript

我有以下几种类型

type ItemDefaultType = object | null | string

interface ItemToString<Item = ItemDefaultType> {
  (item: ItemDefaultType): string;
}

interface AutosuggestState<Item = ItemDefaultType> {
  highlightedIndex: number | null
  inputValue: string | null
  isOpen: boolean
  selectedItem: Item
}

interface AutosuggestProps<Item = ItemDefaultType> extends AutosuggestState<Item> {
  itemToString?: ItemToString<Item>;

  initialSelectedItem?: Item;
  initialInputValue?: string;
  initialHighlightedIndex?: number | null;
  initialIsOpen?: boolean;

  defaultHighlightedIndex?: number | null;
  defaultIsOpen?: boolean;

  id?: string;
  inputId?: string;
  labelId?: string;
  menuId?: string;

  itemCount?: number
}

我想使用预期的签名制作一个getInitialValue函数–

// this has to be done correctly using generics, not been able to do it, read on…
(
  props: Props extends AutosuggestProps,
  stateKey: StateKey extends keyof AutosuggestState
) => AutosuggestState[StateKey] // return the correct type of AutosuggestState property based on which stateKey was passed

getInitialValue的行为如下

// javascript version

const defaultStateValues = {
  highlightedIndex: -1,
  isOpen: false,
  selectedItem: null,
  inputValue: ''
}

function getDefaultValue(props, statePropKey) {
  const defaultPropKey = `default${capitalizeString(statePropKey)}`
  if (defaultPropKey in props) {
    return props[defaultPropKey]
  }
  return defaultStateValues[statePropKey]
}

function getInitialValue(props, statePropKey) {
  if (statePropKey in props) {
    return props[statePropKey]
  }
  const initialPropKey = `initial${capitalizeString(statePropKey)}`
  if (initialPropKey in props) {
    return props[initialPropKey]
  }
  return getDefaultValue(props, statePropKey)
}

我发现很难同时编写getInitialValuegetDefaultValue的类型,使得getInitialValue正确地推断出正确的类型,如下所示–

const selectedItem = getInitialValue(props, 'selectedItem') // selectedItem variable should correctly be inferred as **object | null | string** since that's what its type is in **AutosuggestState** interface

有人可以帮我写类型吗?

1 个答案:

答案 0 :(得分:1)

以下是可能的输入方式,它可能适合您的用例,也可能不适合您

function getDefaultValue<P extends AutosuggestProps, K extends keyof AutosuggestState>(
  props: P,
  statePropKey: K) {
  const defaultPropKey = `default${capitalizeString(statePropKey)}`
  if (defaultPropKey in props) {
    return props[defaultPropKey as K] // assert here
  }
  return defaultStateValues[statePropKey]
}

function getInitialValue<P extends AutosuggestProps, K extends keyof AutosuggestState>(
  props: P, statePropKey: K) {
  if (statePropKey in props) {
    return props[statePropKey]
  }
  const initialPropKey = `initial${capitalizeString(statePropKey)}`
  if (initialPropKey in props) {
    return props[initialPropKey as K] // assert here
  }
  return getDefaultValue(props, statePropKey)
}

我实际上所做的就是使函数在您在伪代码中提到的类型的PK中通用。

您需要做出的一项决定是使用defaultPropKeyinitialPropKey时该怎么做。编译器无法验证是否在属性名称前加上"default""initial"(如果存在)是否会挑选出相同类型的另一个属性。编译器无法在类型级别理解字符串连接(在Github中这样做的建议似乎是microsoft/TypeScript#12754)。如果您可以重构接口,以便将默认值和初始值存储在名为defaultinitial的属性中,它们本身就是具有 same 键属性的对象,那么您可以让编译器弄清楚。假设您采用这种方式,例如,我使用type assertion来表示“对待initialPropKey 好像是{{1}类型的 }”。然后,编译器将假定Kprops[initialPropKey]具有相同的类型。

无论如何,现在将这些函数的返回类型推断为

props[statePropKey]

之所以有意义,是因为它在末尾使用P[K] | { highlightedIndex: number; isOpen: boolean; selectedItem: null; inputValue: string; }[K] ,这是一种具体的类型,不知道是扩展的defaultStateValues类型。可以通过使用conditional返回类型来加强这一点,但是您的示例代码并未显示出这种需要。

让我们看看它是否有效:

P

我觉得合理。好的,希望能有所帮助;祝你好运!

Link to code