获得更有针对性的基于条件类型的函数签名,尝试根据输入缩小到特定输出

阿里汉特

我有以下类型

type ItemDefaultType = object | null | string

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

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

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

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

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

我有以下输入代码


function capitalizeString(string: string) {
  return `${string.slice(0, 1).toUpperCase()}${string.slice(1)}`
}

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

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

在做

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

selectedItem显示返回类型object | null | string | undefined但是根据getInitialValuegetDefaultValue函数的性质,可以肯定的是,至少selectedItem会从defaultStateValues对象返回默认值

  1. 如何编写更有针对性的基于条件类型的函数签名,getInitialValuegetDefaultValue尝试根据输入缩小到特定输出,即selectedItem具有类型ItemDefaultType而不是object | null | string | undefined(实际上是ItemDefaultType | undefined

  2. 有没有更好的方法来编写类型和函数?例如,在这个问题中——

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

回答者建议可能不需要类型断言 -

如果您可以重构您的接口,以便将默认值和初始值存储在名为defaultinitial 的属性中,这些属性本身就是持有相同键属性的对象,那么您可以让编译器弄清楚

是否可以对类型定义和接口进行其他结构更改?

注意:如果您只有时间回答一个,请回答第一个。

带有预写代码的Typescript Playground

杰卡兹

我处理这个问题的第一次尝试是进行提到的结构重构,并使用Exclude实用程序类型来表示undefined在您防范undefined时不可能输出的约束

所以重构看起来像这样:

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

  initial?: {
    highlightedIndex?: number | null;
    inputValue?: string | null;
    isOpen?: boolean;
    selectedItem?: Item;
  }

  default?: {
    highlightedIndex?: number | null;
    inputValue?: string | null;
    isOpen?: boolean;
    selectedItem?: Item;
  }

}

功能如下所示:

function getDefaultValue<
  P extends AutosuggestProps,
  K extends keyof AutosuggestState
>(
  props: P,
  statePropKey: K
) {
  if (props.default && typeof props.default[statePropKey] !== "undefined") {
    const ret = props.default[statePropKey]
    return ret as Exclude<typeof ret, undefined>;
  }
  return defaultStateValues[statePropKey]
}

function getInitialValue<
  P extends AutosuggestProps,
  K extends keyof AutosuggestState
>(
  props: P,
  statePropKey: K
) {
  if (statePropKey in props && typeof props[statePropKey] !== "undefined") {
    const ret = props[statePropKey];
    return ret as Exclude<typeof ret, undefined>;
  }
  if (props.initial && typeof props.initial[statePropKey] !== "undefined") {
    const ret = props.initial[statePropKey];
    return ret as Exclude<typeof ret, undefined>;
  }
  return getDefaultValue(props, statePropKey)
}

这给出了以下结果:

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

也许这对你来说就足够了。


如果您需要一个条件类型签名来尝试预测每个函数中实际发生的分支并输出更紧密的类型,这将以函数实现内部的类型安全性降低为代价,因为编译器不执行此操作自动分析,也不能验证手动条件类型是否与函数的结果相匹配。

无论如何,它可能看起来像这样:

type IdxWithDefault<T, K extends keyof any, D = never> = T extends any ? K extends keyof T ?
  Exclude<T[K], undefined> | (undefined extends T[K] ? D : never) : D : never

该类型IdxWithDefault<T, K, D>尝试查找关键K的类型T如果成功而属性不成功undefined,则计算结果为T[K]否则,它的计算结果为D它分布在联合上,所以如果T[K]string | undefined,那么输出将是string | D

现在函数签名可以使用IdxWithDefault

function getDefaultValue<
  P extends AutosuggestProps,
  K extends keyof AutosuggestState
>(
  props: P,
  statePropKey: K
): IdxWithDefault<
  P["default"], K, IdxWithDefault<
    typeof defaultStateValues, K
  >
>
function getDefaultValue(props: AutosuggestProps, statePropKey: keyof AutosuggestState) {
  if (props.default && typeof props.default[statePropKey] !== "undefined") {
    return props.default[statePropKey]
  }
  return defaultStateValues[statePropKey]
}

function getInitialValue<
  P extends AutosuggestProps,
  K extends keyof AutosuggestState
>(
  props: P,
  statePropKey: K
): IdxWithDefault<
  P, K, IdxWithDefault<
    P["initial"], K, IdxWithDefault<
      P["default"], K, IdxWithDefault<
        typeof defaultStateValues, K>
    >
  >
>;
function getInitialValue(props: AutosuggestProps, statePropKey: keyof AutosuggestState) {
  if (statePropKey in props && typeof props[statePropKey] !== "undefined") {
    return props[statePropKey];
  }
  if (props.initial && typeof props.initial[statePropKey] !== "undefined") {
    return props.initial[statePropKey];
  }
  return getDefaultValue(props, statePropKey)
}

测试它...这是一样的,因为props只知道是AutosuggestProps.

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

但以下输出string类型:

getInitialValue({
  selectedItem: "wheee"
}, "selectedItem"); // string

我想这就是我所能做到的。如果您发现边缘情况不符合您的预期,您可以尝试使用嵌套IdxWithDefault类型。


好的,希望有帮助;祝你好运!

代码链接

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何获取函数将返回值类型缩小到特定子类型而不是返回联合或索引类型

文字输入-缩小到最小

如何强制缩小到打字稿中类型化常量的特定类型?

从git bisect错误中恢复更有针对性吗?

Solr 如何将搜索范围缩小到特定品牌,并在单个查询中获得该品牌的所有兄弟姐妹?

理解有针对性的赋值 C

缩小到具有组织速度的子树

缩小到对象路径

如何将所有元素大小、字体大小缩小到更小的屏幕,就像缩小到 75%

如何编写循环以从输出中删除NULL并将其缩小到所需的长度?

将ArrayList缩小到新大小

将网格容器缩小到行数

如何才能有针对性地定位特定的AppWidgetProvider?

PHP函数根据输入获得相同的输出

有没有一种方法可以通过较早的null / undefined检查将“信号”类型缩小到TypeScript?

为什么打字稿将未知范围缩小到从不进入可达条件块?

打字稿中的类型保护正在缩小到从不打字

如何将Google Apps脚本的范围缩小到特定文件

将名称包括日期的文件缩小到 r 中的特定月份

如何有效地将数组缩小到目标标准差

如何捕获所有关键帧并缩小到320像素宽?

如何从Filter Aggregation返回的文档集中将当前聚合上下文缩小到特定范围?

我不断收到分段错误,但找不到它!我想我把它缩小到一个特定的功能

鉴于有针对性的整个 div 没有使用 jspdf 导出为 pdf?

TfidfVectorizer 将我的数据帧从 799 缩小到 3

为什么numpy核心大小从1.14.0缩小到1.14.1?

将YouTube视频缩小到响应宽度

如何将TableLayout列缩小到minWidth?

SVG文件缩小到5%会导致失真