啊,type-level equality operator。 @MattMcCutchen 提出了一个涉及通用条件类型的solution,它可以很好地检测两种类型何时完全相等,而不仅仅是相互分配。在一个完美的类型系统中,“mutually assignable”和“equal”可能是同一回事,但 TypeScript 并不完美。特别是,any 类型既可分配给任何其他类型,也可从任何其他类型分配,这意味着 string extends any ? true : false 和 any extends string ? true: false 都计算为 true,尽管事实上 string 和 any 并不相同类型。
这是一个 IfEquals<T, U, Y, N> 类型,如果 T 和 U 相等,则计算结果为 Y,否则计算结果为 N。
type IfEquals<T, U, Y=unknown, N=never> =
(<G>() => G extends T ? 1 : 2) extends
(<G>() => G extends U ? 1 : 2) ? Y : N;
让我们看看它的工作原理:
type EQ = IfEquals<any[], [number][], "same", "different">; // "different"
好的,这些被识别为不同的类型。可能还有其他一些极端情况,您认为相同的两种类型被视为不同,反之亦然:
type EQ1 = IfEquals<
{ a: string } & { b: number },
{ a: string, b: number },
"same", "different">; // "different"!
type EQ2 = IfEquals<
{ (): string, (x: string): number },
{ (x: string): number, (): string },
"same", "different">; // "different", as expected, but:
type EQ3 = IfEquals<
{ (): string } & { (x: string): number },
{ (x: string): number } & { (): string },
"same", "different">; // "same"!! but they are not the same,
// intersections of functions are order-dependent
无论如何,给定这种类型,我们可以创建一个生成错误的函数,除非这两种类型以这种方式相等:
/** Trigger a compiler error when a value is _not_ an exact type. */
declare const exactType: <T, U>(
draft: T & IfEquals<T, U>,
expected: U & IfEquals<T, U>
) => IfEquals<T, U>
declare let a: any[]
declare let b: [number][]
// $ExpectError
exactType(a, b) // error
每个参数都有一个与IfEquals<T, U> 相交的类型T 或U(用于泛型参数的类型推断),因此除非T 和U 相等,否则将出现错误。我认为这会给出你想要的行为。
请注意,此函数的参数不是可选的。我真的不知道您为什么希望它们是可选的,但是(至少在打开 --strictNullChecks 的情况下)它会削弱这样做的检查:
declare let c: string | undefined
declare let d: string
exactType(c, d) // no error if optional parameters!
这取决于你。
无论如何希望这会有所帮助。祝你好运!