声明一个以数组为起始点的接口

时间:2019-02-18 04:41:28

标签: typescript typescript-typings

长话短说,出于复杂的原因,我需要能够声明一个接口(使用数组签名)以1开始,或者至少[0]为空。这可能吗?

我已经尝试过:

interface ItemCollection<T> extends Collection
{
    [index: number]: [null, ...Function[]];


    addComp(): void;
}

显然只是说,“ ItemCollection [0 ...]是一个元组”,不是我想要的。

也尝试过:

interface ItemCollection<T> extends Collection
{
    [0]: null;
    [index: number]: Function;
}

但是我只收到一条错误消息,“类型为'null'的属性'[0]'无法分配给数字索引类型为'Function'.ts(2412)” 所以显然不是答案。

我可以使用标准变量来做到这一点:

tmp: [null, ...Function[]];

这很好用,但是不一样。

2 个答案:

答案 0 :(得分:3)

知道了。但这需要很多递归魔术:

首先让我们声明utils从元组中添加/删除元素:

type PrependTuple<A, T extends Array<any>> =
  A extends undefined ? T : 
  (((a: A, ...b: T) => void) extends (...a: infer I) => void ? I : [])

type RemoveFirstFromTuple<T extends any[]> = 
  T['length'] extends 0 ? undefined :
  (((...b: T) => void) extends (a, ...b: infer I) => void ? I : [])

type FirstFromTuple<T extends any[]> =
  T['length'] extends 0 ? undefined : T[0]

然后,我们必须声明将使用N个项目创建元组的utils:

type NumberToTuple<N extends number, L extends Array<any> = []> = {
  true: L;
  false: NumberToTuple<N, PrependTuple<1, L>>;
}[L['length'] extends N ? "true" : "false"];

这将递归地增加L元组的大小,直到达到我们想要的长度为止。

下一步反向元组util(基于相同的想法)

type ReverseTuple<T extends Array<any>, L extends Array<any> = []> = {
  true: L;
  false: ReverseTuple<T, PrependTuple< T[L['length']] , L>>;
}[L['length'] extends T['length'] ? "true" : "false"];

type R4 = ReverseTuple<[1, 2, 3, 4]>; // [4,3,2,1]

最后一个需要使用的工具以减少数量:

type Decrease<I extends number> = RemoveFirstFromTuple<NumberToTuple<I>>['length']

它正在创建具有确切长度的元组并删除第一个元素。结果就是剪切的元组的大小。

cream de la cream:将应用逻辑的迭代器:

type Iter<N extends number, Items extends any[], L extends Array<any> = []> = {
  true: L;
  false: Iter<N, Items, PrependTuple<L['length'] extends N ? unknown : Items[Decrease<L['length']>]  , L>>
}[L["length"] extends N ? "true" : "false"];

以sam方式进行迭代,但是在最后一步(当L['length'] extends N时),我们在unknown之前添加了元组。因为我们增加了最终元组的大小(Result ['length'] === Items ['length'] + 1),所以我们不得不减少索引。

此外,因为我们在前面添加(而不是追加),所以我们必须反转元组。因此最终的泛型将如下所示:

type FromFirstTuple<T extends Array<any>> = ReverseTuple<Iter<PrependTuple<1, T>['length'], T>>

type Result = FromFirstTuple<[1,2,3]> // [unknown, 1, 2, 3]

Playground

答案 1 :(得分:1)

我想这就是您想要的...(也可以考虑将其标记为接受)

type Unshift<T extends any[]> =
    ((a: null, ...b: T) => void) extends ((...ab: infer R) => void)
    ? R : never

type foo = Unshift<[1, 2, 3]> // [null, 1, 2, 3]

Demo