强制TypeScript数组包含给定值的元素 [英] Force TypeScript Array to contain an element of given value

查看:157
本文介绍了强制TypeScript数组包含给定值的元素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to force an argument of type number[] to contain at least one element of value 9.

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

So far I've got:

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]);

链接到TS操场

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

But ofc the type MyType won't include all possible indexes, so I'm trying to write that in some other way. { [index: number]: 9} is not the good way to do that, since it requires all values to be set to 9. I've also tried some combination of mapped types, with no success

我怎么写MyType以便解决这个问题?

How can I write MyType so that it solves this problem?

推荐答案

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

You can indeed use mapped types. Here's how I'd type 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]可以处理这两种情况).有一些涉及到黑魔法.我还希望一些有趣的边缘情况会出现在诸如number,0元素元组等加宽类型周围.最后,我使用了readonly数组,因此人们可以使用

Some of the complexity here has to do with convincing the compiler to infer array literal values as tuple types and number literal values as numeric literal types (having [R] in there deals with both). There's some black magic involved. Also I'd expect some interesting edge cases to crop up around widened types like number, 0-element tuples, etc. Finally, I used readonly arrays so people can use const assertions if they want (as in 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的长度一样多的项.

Okay, the heart of the matter: { [ K in keyof T]: { [P in K]: R } }[number] type is very much like your MyType type alias. If T is [4, 5, 6, 7, 8] and R is 9, then that type becomes [{0: 9}, {1: 9}, {2: 9}, {3: 9}, {4: 9}][number], or {0: 9} | {1: 9} | {2: 9} | {3: 9} | {4: 9}. Notice how it expands to have as many terms as the length of T.

让我们看看它是否有效:

Let's see if it works:

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

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

Looks good. Hope that helps; good luck!

链接到代码

这篇关于强制TypeScript数组包含给定值的元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆