Gan*_*V S 2 types typescript typescript-typings typescript-types
类型应该检测数组是否有重复的项目并在打字稿中引发错误?
type UniqueArray = [
// How to implement this?
]
const a:UniqueArray = [1, 2, 3] // success
const b:UniqueArray = [1, 2, 2] // error
Run Code Online (Sandbox Code Playgroud)
PS:我目前正在使用JS删除重复项,但是,好奇是否可以使用手写脚本类型捕获此错误?
是的!TypeScript 4.1(在撰写本文时处于测试阶段)有一种方法。这是如何:
const data = ["11", "test", "tes", "1", "testing"] as const
const uniqueData: UniqueArray<typeof data> = data
type UniqueArray<T> =
T extends readonly [infer X, ...infer Rest]
? InArray<Rest, X> extends true
? ['Encountered value with duplicates:', X]
: readonly [X, ...UniqueArray<Rest>]
: T
type InArray<T, X> =
T extends readonly [X, ...infer _Rest]
? true
: T extends readonly [X]
? true
: T extends readonly [infer _, ...infer Rest]
? InArray<Rest, X>
: false
Run Code Online (Sandbox Code Playgroud)
如果相同的值出现多次,您将收到编译器错误。
这可能在编译时起作用的唯一方法是,如果数组是由文字组成的元组。例如,以下一些数组具有相同的运行时值,但在TypeScript中具有不同的类型:
const tupleOfLiterals: [1, 2, 2] = [1, 2, 2];
const tupleOfNonLiterals: [number, number, number] = [1, 2, 2];
const arrayOfLiterals: (1 | 2)[] = [1, 2, 2];
const arrayOfNonLiterals: number[] = [1, 2, 2];
Run Code Online (Sandbox Code Playgroud)
只有第一个tupleOfLiterals
元素会表现出您想要的行为……编译器会意识到它恰好具有3个元素,其中两个是同一类型。在所有其他情况下,编译器都不了解发生了什么。因此,如果要传递的数组是从其他函数或API等获得的,而这些数组的类型类似于number[]
,则答案只是“不,您不能这样做”。
如果您正在获取文字的元组...比如说,从开发人员那里使用您的代码作为库,那么您就有机会获得一些有用的东西,但是它很复杂并且可能很脆弱。这是我可能的方法:
首先,我们提出一种类似于无效类型的东西,而TypeScript没有。这个想法是一种类型,不能给它赋值(例如never
),但是当编译器遇到它时会产生一个自定义错误消息。以下内容并非十全十美,但是如果您斜视,它会产生可能合理的错误消息:
type Invalid<T> = Error & { __errorMessage: T };
Run Code Online (Sandbox Code Playgroud)
现在我们代表UniqueArray
。它不能作为具体类型来完成(所以no const a: UniqueArray = ...
),但是我们可以将其表示为传递给辅助函数的一般约束。无论如何,AsUniqueArray<A>
这是一个候选数组类型A
,A
如果唯一,则返回,否则返回另一个数组,该数组中重复出现错误消息的地方:
type AsUniqueArray<
A extends ReadonlyArray<any>,
B extends ReadonlyArray<any>
> = {
[I in keyof A]: unknown extends {
[J in keyof B]: J extends I ? never : B[J] extends A[I] ? unknown : never
}[number]
? Invalid<[A[I], "is repeated"]>
: A[I]
};
Run Code Online (Sandbox Code Playgroud)
这使用了许多映射和条件类型,但实际上它遍历了数组,并查看数组中是否有其他元素与当前元素匹配。如果是这样,则出现错误信息。
现在为助手功能。另一个难题是,默认情况下,像这样的函数doSomething([1,2,3])
将被[1,2,3]
视为a number[]
而不是[1,2,3]
文字的元组。没有简单的方法可以解决这个问题,因此我们必须使用奇怪的魔术(请参阅有关该魔术的讨论链接):
type Narrowable =
| string
| number
| boolean
| object
| null
| undefined
| symbol;
const asUniqueArray = <
N extends Narrowable,
A extends [] | ReadonlyArray<N> & AsUniqueArray<A, A>
>(
a: A
) => a;
Run Code Online (Sandbox Code Playgroud)
现在,asUniqueArray()
仅在运行时返回其输入,但是在编译时,它将仅接受它认为唯一的数组类型,并且如果存在重复,它将在问题元素上出现错误:
const okay = asUniqueArray([1, 2, 3]); // okay
const notOkay = asUniqueArray([1, 2, 2]); // error!
// ~ ~
// number is not assignable to Invalid<[2, "is repeated"]> | undefined
Run Code Online (Sandbox Code Playgroud)
万岁,这就是您想要的,对吧?从一开始的警告仍然存在,因此,如果最终得到的数组已经被扩大了(非元组或非文字),则将出现不良行为:
const generalArray: number[] = [1, 2, 2, 1, 2, 1, 2];
const doesntCareAboutGeneralArrays = asUniqueArray(generalArray); // no error
const arrayOfWideTypes: [number, number] = [1, 2];
const cannotSeeThatNumbersAreDifferent = asUniqueArray(arrayOfWideTypes); // error,
// Invalid<[number, "is repeated"]>
Run Code Online (Sandbox Code Playgroud)
无论如何,所有这些可能对您来说都不值得,但我想证明类型系统可以提供某种接近此的方法。希望能有所帮助;祝好运!
小智 6
与批准的答案非常相似,但InArray
经过简化和内联。
type IsUnique<A extends readonly unknown[]> =
A extends readonly [infer X, ...infer Rest]
? X extends Rest[number]
? [never, 'Encountered value with duplicates:', X] // false
: IsUnique<Rest>
: true;
type IsInArray<A extends readonly unknown[], X> = X extends A[number] ? true : false;
type TestA = IsUnique<["A","B","C"]>; // true
type TestB = IsUnique<["A","B","B"]>; // [never, "Encountered value with duplicates:", "B"]
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
169 次 |
最近记录: |