Force TypeScript Array to contain an element of given value

前端 未结 1 963
太阳男子
太阳男子 2021-01-06 08:56

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         


        
相关标签:
1条回答
  • 2021-01-06 09:22

    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;
    

    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)).

    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!

    Link to code

    0 讨论(0)
提交回复
热议问题