【问题标题】:Force TypeScript Array to contain an element of given value强制 TypeScript 数组包含给定值的元素
【发布时间】:2020-01-17 06:55:49
【问题描述】:

我正在尝试强制 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 类型不会包含所有可能的索引,所以我试图以其他方式编写它。 { [index: number]: 9} 不是这样做的好方法,因为它需要将 all 值设置为 9。我也尝试了一些映射类型的组合,但没有成功

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

【问题讨论】:

    标签: arrays typescript generics types typescript-generics


    【解决方案1】:

    您确实可以使用映射类型。这是我输入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(如@987654332 @)。

    好的,问题的核心:{ [ 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

    【讨论】:

    • 关于| [R] 部分的评论:此模式为mentioned at least once by Hejlsberg himself here。另请参阅answer to a related question
    • 您好,感谢您分享这个很棒的解决方案。如何为数组定义类似的要求(没有函数声明)。我想让这项工作:const myList: ArrayWithForcedItem&lt;'bar'&gt; = ['foo', 'bar']。如果我尝试使用您的解决方案,我仍然需要每次都明确定义泛型类型:const y: ArrayForcedItem&lt;'bar', ["bar", "house", "mouse"]&gt; = ["bar", "house", "mouse"]
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多