强制TypeScript数组包含给定值的元素

时间:2019-09-16 12:21:34

标签: arrays typescript generics types typescript-generics

我正试图强制类型为number[]的参数包含至少一个值为9的元素。

到目前为止,我已经得到:

type MyType<Required> = { 0: Required } | { 1: Required } | { 2: Required };

declare function forceInArray<
    Required extends number,
    Type extends number[] & MyType<Required>
>(
    required: Required,
    input: Type
): void;

// should fail type-checking
forceInArray(9, []);
forceInArray(9, [1, 2]);
forceInArray(9, { 0: 9 });

// should type-check correctly
forceInArray(9, [9]);
forceInArray(9, [9, 9]);
forceInArray(9, [9, 2, 3, 4]);
forceInArray(9, [1, 9, 3, 4]);
forceInArray(9, [1, 2, 9, 4]);
forceInArray(9, [1, 2, 3, 9]);

Link to TS playground

但是类型MyType的ofc不会包含所有可能的索引,因此我试图以其他方式编写。 { [index: number]: 9}不是这样做的好方法,因为它需要将所有值设置为9。我还尝试了一些映射类型的组合,但没有成功

我该如何写MyType来解决这个问题?

1 个答案:

答案 0 :(得分:2)

您确实可以使用映射类型。这是我输入forceInArray()的方式:

declare function forceInArray<
  R extends number,
  T extends (ReadonlyArray<number> | readonly [R]) &
    { [K in keyof T]: { [P in K]: R } }[number]
>(required: R, input: T): void;

这里的一些复杂性与说服编译器将数组文字值推断为元组类型,将数字文字值推断为数字文字类型有关(其中的[R]处理这两者)。有一些black magic involved。我也希望一些有趣的边缘情况会出现在诸如number,0元素元组等加宽类型周围。最后,我使用了readonly数组,因此人们可以使用const assertions想要(如forceInArray(9, [1,2,9] as const))。

好吧,关键是:{ [ K in keyof T]: { [P in K]: R } }[number]类型非常类似于您的MyType类型别名。如果T[4, 5, 6, 7, 8],而R9,则该类型变为[{0: 9}, {1: 9}, {2: 9}, {3: 9}, {4: 9}][number]{0: 9} | {1: 9} | {2: 9} | {3: 9} | {4: 9}。请注意,它如何扩展为具有与T的长度一样多的项。

让我们看看它是否有效:

forceInArray(9, []); // error
forceInArray(9, [1, 2]); // error
forceInArray(9, { 0: 9 }); // error

forceInArray(9, [9]); // okay
forceInArray(9, [9, 9]); // okay
forceInArray(9, [9, 2, 3, 4]); // okay
forceInArray(9, [1, 9, 3, 4]); // okay
forceInArray(9, [1, 2, 9, 4]); // okay
forceInArray(9, [1, 2, 3, 9]); // okay
forceInArray(9, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); // okay

看起来不错。希望能有所帮助;祝你好运!

Link to code