首页 > 解决方案 > 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用预期的签名创建一个函数——</p>

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

我发现很难编写两者的类型,getInitialValue以便getDefaultValue正确getInitialValue推断出正确的类型,如下所示 -</p>

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

有人可以帮我写类型吗?

标签: javascripttypescript

解决方案


这是一个可能的类型,可能适合也可能不适合您的用例:

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

我真正所做的只是使函数在您的伪代码中提到的类型 中具有通用P性。K

defaultPropKey您需要做出的一个决定是使用和时要做什么initialPropKey。编译器无法验证添加 "default"或添加"initial"到属性名称(如果存在)是否会挑选出相同类型的另一个属性。编译器不理解类型级别的字符串连接(在 Github 中这样做的建议似乎是microsoft/TypeScript#12754)。如果您可以重构您的接口,以便将默认值和初始值存储在命名的属性中default,并且initial这些属性本身就是持有相同键属性的对象,那么您可以让编译器弄清楚。假设您保持这种方式,我使用了类型断言来表示,例如,“对待initialPropKey 好像它的类型是K"。然后编译器将假定它props[initialPropKey]的类型与props[statePropKey].

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

P[K] | {
    highlightedIndex: number;
    isOpen: boolean;
    selectedItem: null;
    inputValue: string;
}[K]

这是有道理的,因为它defaultStateValues在最后使用,这是一种不知道是扩展类型的具体P类型。这可能会通过使用条件返回类型来加强,但您的示例代码没有显示需要这样做。

让我们看看它是否有效:

declare const props: AutosuggestProps;
const selectedItem = getInitialValue(props, 'selectedItem');
// const selectedItem: ItemDefaultType

在我看来是合理的。好的,希望有帮助;祝你好运!

链接到代码


推荐阅读