typescript - 如果已知数组类型,则保留数组类型

标签 typescript typescript-generics

我正在尝试这样做,以便如果类型 number | number[] 已知它可以传递给通用函数,该函数验证它是一个数组,而不会丢失数组类型是 number[] 的事实。简化示例:

function assertArray(value: unknown): unknown[] {
  if (Array.isArray(value)) {
    return value;
  }
  throw new Error();
}

用法:

const value: number | number[] = [1, 2, 3];
const verified = assertArray(value);

在上面的示例中,我希望 assertArray 的返回类型足够智能,能够确定它正在返回 number[],因为它传递了 number | number[] 并确保它是唯一的数组选项 number[]。但是,它当前返回unknown[]。我想做到这一点,同时保持将任何类型的数组传递给assertArray的灵活性,并让它返回正确类型的数组(如果可以确定)。

最佳答案

当您使用像 narrowing 这样的 type guard function 时发生的那种 Array.isArray() 很难模拟。您想要实现这一目标的程度实际上取决于您的用例。

本质上,当您想要将类型 T 缩小为另一种类型 U 时,编译器必须选择将 T 中的 union 过滤到可分配给 U 的那些成员(即 Extract<T, U> )还是 0x104 567916 TU (即 T & U ) ,或这些的某种组合。从概念上讲,这些应该是相似的,但实际上,存在各种可观察到的差异。

在您的情况下,如果 Uunknown[] ,那么这就是人们可能期望的,并且会通过各种基本方法发生:

<表类=“s-表”> <标题> T 预期 过滤器联合 相交 <正文> number | number[] number[] number[]👍 number[] & unknown[]👎 unknown unknown[] never👎 unknown[]👍 intersecting ArrayLike<string> string[]👎 never🤷

(ArrayLike<string> & unknown[] 类型仅意味着它是具有数字 ArrayLike<T> 属性和数字 length 的类型,以便您可以在任何数字索引属性中读取和写入 T 类型的值。它是 index signature 。)

很明显,这两种基本方法本身都不能完美地工作。这意味着我们需要一些更复杂的东西,但我们仍然可能无法得到我们想要的东西。


这是一种可能的前进方向:

function assertArray<T extends unknown>(value: T) {
    if (Array.isArray(value)) {
        return value as
            T extends unknown[] ? T : (T & unknown[]);
    }
    throw new Error();
}

返回类型为 T extends unknown[] ? T : T & unknown[] ,即 array-like ,用于过滤联合体,但它不会删除不匹配的联合体成员,而是将这些成员与 unknown[] 相交。这会导致以下行为:

<表类=“s-表”> <标题> T 预期 返回类型为assertArray() <正文> number | number[] number[] number[]👍 unknown unknown[] unknown[]👍 ArrayLike<string> string[] ArrayLike<string> & unknown[]🤷

所以,这非常好,并且涵盖了您问题中提到的用例。

但它并不完美,因为人们可能希望在成功调用 ArrayLike<string> 后将 Array<string> 缩小到 Array.isArray(x)。并且类数组类型的交集并不总是有用。但这是由 x is unknown[] 形式的类型保护函数产生的,所以也许它已经足够好了!

distributive conditional type

关于typescript - 如果已知数组类型,则保留数组类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76784609/

相关文章:

typescript - 为什么条件类型中的 "infer"会改变返回类型?

angular - 错误 TS2345 : Argument of type 'Menu' is not assignable to parameter of type

TypeScript - 删除具有特定类型的所有属性

typescript - 基于另一个属性的通用映射类型属性

javascript - 在浏览器和 Node 之间共享 Tree-shaking TypeScript/JavaScript

typescript - typescript 中的嵌套泛型

javascript - 以元组数组作为参数的函数,其中每个元组的第一个元素是第二个元组的父类型

angular - 如何从子组件更改父组件的变量

javascript - typescript 。 ESLint 不适用于类型

javascript - 对象中的 typescript 符号