在 Typescript 中,为数组定义一个类型,其中第一个元素比其他元素更具体

Posted

技术标签:

【中文标题】在 Typescript 中,为数组定义一个类型,其中第一个元素比其他元素更具体【英文标题】:In Typescript, define a type for an array where the first element is more specific than the rest 【发布时间】:2017-11-08 04:24:57 【问题描述】:

我想为一个数组定义一个类型,它的第一个元素是特定类型(例如函数),其余元素是空类型。例如:

type FAs = [Function, , , , ...]; // pseudo code

这样的事情可能吗?

目的是提供这样的单参数函数:

const myCaller = ([fun, ...args]: FAs) => fun.apply(args);

另一种方法是对myCaller 使用两个参数,如下所示:

const myCaller = (fun: Function, args: any[]) => fun.apply(args);

但出于美学原因,我更喜欢使用单个参数。我还想知道类型系统是否支持可以说是任意长度的元组。也许出于我不理解的计算机科学原因,这种事情是不可取的。

【问题讨论】:

有些相关:github.com/Microsoft/TypeScript/issues/212 【参考方案1】:

我很确定这是 Typescript 2.3 中你能做到的最好的。例如,你可以在 lodash 中看到这样的类型。

interface IMyCaller 
  <R>([fn]: [() => R]): R;
  <R,A>([fn, a]: [(a: A) => R, A]): R;
  <R,A,B>([fn, a, b]: [(a: A, b: B) => R, A, B]): R;
  <R,A,B,C>([fn, a, b, c]: [(a: A, b: B, c: C) => R, A, B, C]): R;
  // keep adding these until you get tired


const myCaller: IMyCaller = ([fun, ...args]) => fun.apply(args);

【讨论】:

这也是我的想法,但 Typescript 对未明确定义的元素使用联合类型非常有帮助。请参阅接受的答案以获得出色的描述,以及一个重要的警告。 你是对的,如果你不需要严格的输入,它会很有帮助。我想我被带走了,并没有非常关注你的问题:)【参考方案2】:

如果你定义

type FAs = [Function, ];

那么FAs 类型的值将需要Function 类型的第一个元素、 类型的第二个元素以及Function | 类型的后续元素。这就是 TypeScript 文字数组类型的工作方式。来自TS docs:

当访问已知索引集之外的元素时,将使用联合类型:

这应该可以满足您的所有需求除了,因为您可以将Function-typed 值作为数组的第三个元素等传递。但实际上无论如何都是这样,因为Function 兼容。

没有办法解决这个问题。在 TS 中没有办法定义一个数组类型,其中前 n 个元素属于某些特定类型,并且存在任意数量的其他特定类型的剩余元素。

我还想知道类型系统是否支持可以说是任意长度的元组。

实际上,类型系统支持任意长度的元组。如果你说

type Tuple = [number, number];

此类型与任何长度为 2或更大且包含数字的数组兼容。如果你说

type Tuple = [string, number];

此类型与长度为 2或更长的任何数组兼容,该数组的第一个元素为字符串,第二个元素为数字,第三个元素为字符串或数字,依此类推。我不会将这种行为的原因称为“基于计算机科学”;更重要的是 TS 检查什么是可行的。

另一种方法

interface Arglist 
  [index: number]: object;
  0: Function;


const a1: Arglist = [func];
const a2: Arglist = [22];                  // fails
const a3: Arglist = [func, "foo"];         // fails
const a4: Arglist = [func, obj];
const a5: Arglist = [func, obj, obj];

【讨论】:

我不知道 Typescript 会为已知索引集之外的元素使用联合类型,尽管我已经多次阅读该文档页面。感谢您的清晰解释和文档链接。 不幸的是,这对我使用 Typescript 3.0.3 不起作用。当我声明一个使用这种类型的变量时,它抱怨长度不匹配。至于替代方法,我想使用地图、过滤器等,因此不得不修改接口以包含这些定义。 @FTWinstone 上面的 type 声明具有任意长度,需要在结果数组中包含 2 个或更多元素。此外,第二个之后的任何元素的类型将不限于string|number 而不仅仅是number。要使类型定义更接近上面的接口,您可以使用type myType = [string, ...number];,这意味着第一个元素必须是字符串,而任何其他元素,如果有,必须是数字。 看起来这个答案现在已经过时了?这个问题有newer 的答案,including TypeScript 4.0。【参考方案3】:

在当前版本的 Typescript 中,这可以使用数组扩展:

type FAs = [Function, ...Array&lt;&gt;]

它支持从1到n的任意长度(第一个元素是必需的)。

【讨论】:

【参考方案4】:

我还想知道类型系统是否支持可以说是任意长度的元组。

自 TS 4.0 起,您可以使用variadic tuple types。 例如,以类型安全的方式断言 [func, ...&lt;proper func args&gt;]

type FAs<A extends unknown[], R> = [(...args: A) => R, ...A]

const myCaller = <A extends unknown[], R>([fn, ...args]: FAs<A, R>) =>
    fn.apply(null, args)
例子:
const fn1 = (a1: string, a2: number) => true

const r1 = myCaller([fn1, "foo", 42]) // OK, r1 has type `boolean`
const r2 = myCaller([fn1, "foo", "bar"]) // error, `bar` has wrong type
const r3 = myCaller([fn1, "foo"]) // error, not enough arguments

Playground

【讨论】:

【参考方案5】:
type First<T> = T extends [infer U, ...any[]] ? U : any;
type F = First<[number, boolean, string]> // number

【讨论】:

能有反转版type Rest&lt;T&gt;吗? type F = Rest&lt;[number, boolean, string]&gt; // [boolean, string]【参考方案6】:

假设您想要一个至少有两个元素的Array,第一个是A 类型,然后是B 类型。 更通用的方法是定义交叉类型如下:

type A = string
type B = number

// ensures that there are at least two elements
interface Foo 
  0: A;
  1: B;


// ensures that the first element is of type A and any after that of type B
type Bar = [A, ...B[]]

type MyArrayType = Foo & Bar

let a: MyArrayType

a = ['hello world', 1] // works
a = ['hello world', 1, 2] // works
a = ['hello world'] // fails
a = ['hello', 'world'] // fails

请注意,即使 A 不是 B 的子集,这也有效。接受的答案需要这样做。

我意识到原始问题不需要此约束。但是,由于我遇到了具有这些限制的线程,它可能会对其他人有所帮助。

【讨论】:

以上是关于在 Typescript 中,为数组定义一个类型,其中第一个元素比其他元素更具体的主要内容,如果未能解决你的问题,请参考以下文章

在 TypeScript 中定义不同泛型类型的数组

TypeScript:从字符串数组定义联合类型

如何在 TypeScript 中迭代自定义文字类型?

TypeScript,接口(Interfaces),对象、数组和函数的类型

TypeScript类型断言数组的类型函数的类型

如何在 TypeScript 中创建自定义类型