强制 TypeScript 数组包含给定值的元素
Force TypeScript Array to contain an element of given value
我试图强制 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]);
但是 MyType
类型不会包含所有可能的索引,所以我正在尝试以其他方式编写它。 { [index: number]: 9}
不是这样做的好方法,因为它需要将 所有 值设置为 9
。我也试过一些映射类型的组合,但没有成功
我怎样写MyType
才能解决这个问题?
您确实可以使用映射类型。这是我输入 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. 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 如果他们想要(如 forceInArray(9, [1,2,9] as const)
)。
好的,问题的核心:{ [ K in keyof T]: { [P in K]: R } }[number]
类型非常像您的 MyType
类型别名。如果 T
是 [4, 5, 6, 7, 8]
并且 R
是 9
,则该类型变为 [{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
看起来不错。希望有所帮助;祝你好运!
我试图强制 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]);
但是 MyType
类型不会包含所有可能的索引,所以我正在尝试以其他方式编写它。 { [index: number]: 9}
不是这样做的好方法,因为它需要将 所有 值设置为 9
。我也试过一些映射类型的组合,但没有成功
我怎样写MyType
才能解决这个问题?
您确实可以使用映射类型。这是我输入 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. 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 如果他们想要(如 forceInArray(9, [1,2,9] as const)
)。
好的,问题的核心:{ [ K in keyof T]: { [P in K]: R } }[number]
类型非常像您的 MyType
类型别名。如果 T
是 [4, 5, 6, 7, 8]
并且 R
是 9
,则该类型变为 [{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
看起来不错。希望有所帮助;祝你好运!