如何测试两种类型是否完全相同

ale*_*son 1 typescript

这是我的第一次尝试:( 游乐场链接)

/** Trigger a compiler error when a value is _not_ an exact type. */
declare const exactType: <T, U extends T>(
    draft?: U,
    expected?: T
) => T extends U ? T : 1 & 0

declare let a: any[]
declare let b: [number][]

// $ExpectError
exactType(a, b)
Run Code Online (Sandbox Code Playgroud)

相关:https://github.com/gcanti/typelevel-ts/issues/39

Jos*_*one 9

我写了一个库tsafe,它可以让你做到这一点。

在此输入图像描述

感谢@jcalz,您的回答对实现这一目标有很大帮助!

  • [请将代码/日志/错误的图像替换为纯文本版本。](https://meta.stackoverflow.com/a/285557/2887218) (5认同)

小智 7

如果您正在寻找一种不依赖任何第三方库的纯打字稿解决方案,那么这个应该适合您

export function assert<T extends never>() {}
type TypeEqualityGuard<A,B> = Exclude<A,B> | Exclude<B,A>;
Run Code Online (Sandbox Code Playgroud)

和用法就像

assert<TypeEqualityGuard<{var1: string}, {var1:number}>>(); // returns an error
assert<TypeEqualityGuard<{var1: string}, {var1:string}>>(); // no error
Run Code Online (Sandbox Code Playgroud)


jca*_*alz 5

啊,类型级相等运算符.@MattMcCutchen提出了一个涉及通用条件类型的解决方案,它可以很好地检测两种类型何时完全相等,而不是相互可分配.在完美的声音类型系统中,"可相互分配"和"相等"可能是相同的,但TypeScript不是完美的声音.特别是,该any类型既是分配到和分配的任何其他类型的,也就是说,string extends any ? true : falseany extends string ? true: false两个评估到true,尽管stringany是不是同一类型.

这是一个IfEquals<T, U, Y, N>评估Yif TU是否相等的类型,N否则.

type IfEquals<T, U, Y=unknown, N=never> =
  (<G>() => G extends T ? 1 : 2) extends
  (<G>() => G extends U ? 1 : 2) ? Y : N;
Run Code Online (Sandbox Code Playgroud)

让我们看看它是否有效:

type EQ = IfEquals<any[], [number][], "same", "different">; // "different"
Run Code Online (Sandbox Code Playgroud)

好的,那些被认为是不同的类型.可能存在一些其他边缘情况,其中您认为相同的两种类型被视为不同,反之亦然:

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
Run Code Online (Sandbox Code Playgroud)

无论如何,给定这种类型我们可以创建一个生成错误的函数,除非这两种类型以这种方式相等:

/** 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
Run Code Online (Sandbox Code Playgroud)

每个参数都有一个类型TU(与泛型参数的类型推断)相交,IfEquals<T, U>因此除非T并且U相等,否则会出现错误.我想,这给了你想要的行为.

请注意,此函数的参数不是可选的.我真的不知道为什么你希望它们是可选的,但是(至少在--strictNullChecks开启时)它会削弱检查:

declare let c: string | undefined
declare let d: string
exactType(c, d) // no error if optional parameters!
Run Code Online (Sandbox Code Playgroud)

如果重要的话,这取决于你.

无论如何希望有所帮助.祝好运!


ale*_*son 5

编辑:最精致的版本可以在这里找到

这是迄今为止我发现的最强大的解决方案:

// prettier-ignore
type Exact<A, B> = (<T>() => T extends A ? 1 : 0) extends (<T>() => T extends B ? 1 : 0)
    ? (A extends B ? (B extends A ? unknown : never) : never)
    : never

/** Fails when `actual` and `expected` have different types. */
declare const exactType: <Actual, Expected>(
    actual: Actual & Exact<Actual, Expected>,
    expected: Expected & Exact<Actual, Expected>
) => Expected
Run Code Online (Sandbox Code Playgroud)

感谢@jcalz 指出正确的方向!