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

时间:2018-12-16 23:52:46

标签: typescript

这是我的第一次尝试:(playground link

/** 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)

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

4 个答案:

答案 0 :(得分:5)

啊,type-level equality operator。 @MattMcCutchen提出了一个solution,涉及泛型条件类型,它在检测两种类型何时完全相等(而不是只能相互赋值)时做得很不错。在完美声音类型的系统中,“相互分配”和“相等”可能是同一回事,但是TypeScript并不是完美声音。特别是,any类型既可以分配给任何其他类型,也可以从任何其他类型分配,这意味着reduceByKeystring extends any ? true : false都求值为any extends string ? true: false,尽管事实{{1 }}和true的类型不同。

这是一种string类型,如果anyIfEquals<T, U, Y, N>相等,则求值为Y,否则为T

U

让我们看看它是否有效:

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 (用于通用参数的类型推断)与T相交,因此除非U和{ {1}}相等。我认为,这给出了您想要的行为。

请注意,此函数的参数不是可选的。我真的不知道为什么您希望它们是可选的,但是(至少在打开IfEquals<T, U>的情况下)会削弱这样做的检查力:

T

这很重要,取决于您。

无论如何,希望能有所帮助。祝你好运!

答案 1 :(得分:1)

编辑:可以找到最精致的版本here

这是到目前为止我发现的最可靠的解决方案:

// 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

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

答案 2 :(得分:0)

迄今为止我见过的最强大的 item: { timeStamp: new Date(), description: '' category: {id: '', categoryName: ''} } (虽然仍然不完美)是这个:

expenses.map (expense =>
     <Button size="sm" color="secondary" onClick={ () => this.update(expense.id)}>Update</Button>

例如,async update(id) { // I want to populate the form with the data from expense.id the user clicked on const response = await fetch(`/api/expenses/${id}`); // GET endpoint from Spring Boot const body = await response.json(); this.setState( {item: body}); // Handling PUT request await fetch(`api/expenses/${id}`, { method: 'PUT', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', } }); updatedItem = {...this.state.item}; // Getting the updated item this.setState({item: updatedItem}); // I want to be able to update the state like this this.props.history.push('/expense'); } 失败。

在这里找到: https://github.com/Microsoft/TypeScript/issues/27024#issuecomment-845655557

答案 3 :(得分:-1)

我们应根据问题采取不同的方法。例如,如果我们知道要与任何数字进行比较,则可以使用typeof()

例如,如果我们在比较接口,则可以使用以下方法:

function instanceOfA(object: any): object is A {
    return 'member' in object;
}