我想让这两个功能通用化:

/**
 * Unpack an element if it is packed.
 * @param data - Data from which to unpack.
 */
export const unpack = (
  data: undefined | string | string[],
  index: number = 0,
): undefined | string => (Array.isArray(data) ? data[index] : data);

/**
 * Pack an element if it is unpacked.
 * @param data - Data to pack.
 */
export const pack = (
  data: undefined | string | string[],
): undefined | string[] =>
  Array.isArray(data) || data === undefined ? data : [data];

这是我的try :

/**
 * Unpack an element if it is packed.
 * @param data - Data from which to unpack.
 */
export const unpack = <T,>(
  data: T,
  index: number = 0,
): T extends unknown[] ? T[number] : T =>
  Array.isArray(data) ? data[index] : data;

/**
 * Pack an element if it is unpacked.
 * @param data - Data to pack.
 */
export const pack = <T,>(data: T): T extends unknown[] | undefined ? T : T[] =>
  Array.isArray(data) || data === undefined ? data : [data];

TypeScript Playground link

对于通用unpack函数,TypScript没有抱怨.然而,对于通用pack函数,TypScript会生成此错误:

Type 'T | (T & ({} | null))[]' is not assignable to type 'T extends unknown[] | undefined ? T : T[]'.
  Type 'T' is not assignable to type 'T extends unknown[] | undefined ? T : T[]'.

通用unpack函数是否正确输入?如何修复通用pack函数的类型?

推荐答案

TypScript目前(截至TS 5.4)无法使用control flow analysis直接影响generic个类型参数. 内部

const pack = <T,>(data: T): T extends unknown[] | undefined ? T : T[] =>
  Array.isArray(data) || data === undefined ? data : [data];

类型判断器可能可以理解,在条件表达的真分支中,data属于类型T & (any[] | undefined).但类型参数T不受影响. 因此,在这种情况下返回data将无法输入check,因为您将T & (any[] | undefined)分配给T extends unknown[] | undefined ? T : T[]. 您想说勾选data直接意味着T.但到目前为止,还没有实现这样的事情,而且正确地做到至少有些棘手(例如,如果您有一个类型T extends string的值t并且您判断了t === "a",则这并不意味着T就是"a".T可以是"a" | "b" | "c",甚至只是string. 您所知道的只是"a"可以分配给T,反之亦然).

因此,以编译器可以准确验证其安全性的方式实现返回conditional type的通用函数几乎是不可能的.

当前开放的对此进行改进的功能请求为microsoft/TypeScript#33014microsoft/TypeScript#33912.在不久的将来,这里可能会有进展,因为它在TypScript 5.5迭代计划中的microsoft/TypeScript#57475处提到了.但目前它还不是语言的一部分.


所有这一切意味着,如果您想实现返回条件类型的通用函数,则可能需要在实现中使用类型安全放松功能,例如type assertionthe any type或两者:

const pack = <T,>(data: T): T extends unknown[] | undefined ? T : T[] =>
  Array.isArray(data) || data === undefined ? data : [data] as any;

请注意,您的unpack函数并不明确需要这个,因为any悄悄进入其中. 在Array.isArray(data) ? data[index] : data的真分支中,data的类型变成了T & any[],因此data[index]变成了T[number] & any,而any则把整个事物变成了any. 也许Array.isArray()应该缩小到unknown[]而不是any[],但这会打破很多人的代码(参见microsoft/TypeScript#43865),所以事情就是这样.

因此,unpack()并不是没有错误地编译,因为TypScript实际上验证了它是安全的.就是该实现被any"感染"了.事实上,您可能会犯一个明显的错误,而编译器不会注意到:

const unpack = <T,>(
  data: T,
  index: number = 0,
): T extends unknown[] ? T[number] : T =>
  Array.isArray(data) ? data[index] : "WHAAAAA"; // <-- 🤔

所有这些都是说,每当你有一个通用条件类型时,你就需要格外小心地安全地实现事情,必要时使用类型断言或any来平息编译器的警告,也许将来会有更好的东西出现.

Playground link to code

Typescript相关问答推荐

如何传递基于TypScript中可变类型的类型?

如何在类型脚本中声明对象其键名称不同但类型相同?

Typescript对每个属性具有约束的通用类型

类型是工作.但它失go 了正确变体的亮点..为什么?或者如何增强?

如何使打包和拆包功能通用?

为什么typescript不能推断类的方法返回类型为泛型''

TypeScrip:基于参数的条件返回类型

如何使用一个字段输入对象,其中每个键只能是数组中对象的id属性,而数组是另一个字段?

使用2个派生类作为WeakMap的键

从子类集合实例化类,同时保持对Typescript中静态成员的访问

如何按属性或数字数组汇总对象数组

tailwind css未被应用-react

必需的输入()参数仍然发出&没有初始值设定项&q;错误

字符串文字联合的分布式条件类型

将类型脚本函数返回类型提取到VSCode中的接口

在对象数组中设置值

有没有一种简单的方法可以访问Keyboard Shortcuts JSON文件中列出的每个命令的显示名称(标题)?

如何将 MUI 主题对象的自定义属性与情感样式组件中的自定义props 一起使用?

带有 Select 器和映射器的Typescript 泛型

如何在 ngFor 循环中以Angular 正确动态渲染组件