-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature request]type level equal operator #27024
Comments
Here's a working implementation: /**
* Tests if two types are equal
*/
export type Equals<T, S> =
[T] extends [S] ? (
[S] extends [T] ? true : false
) : false
; The only problem is that |
@AlCalzone example: type X=Equals<{x:any},{x:number}>;//true |
|
Here's a solution that makes creative use of the assignability rule for conditional types, which requires that the types after export type Equals<X, Y> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? true : false; This passes all the tests from the initial description that I was able to run except H, which fails because the definition of "identical" doesn't allow an intersection type to be identical to an object type with the same properties. (I wasn't able to run test E because I don't have the definition of |
Thank you |
It work, but how? |
@jituanlin AFAIK it relies on conditional types being deferred when
|
@AlCalzone It seems that function overloads do not work. type F = (x: 0, y: null) => void
type G = (x: number, y: string) => void
type EqEq<T, S> = [T] extends [S] ? ([S] extends [T] ? true : false) : false
// Function type intersection is defined as overloads in TypeScript.
type OF1 = EqEq<F & G, G & F> // true
type OF3 = EqEq<{ (x: 0, y: null): void; (x: number, y: null): void }, { (x: number, y: null): void; (x: 0, y: null): void }> // true Function overloads works: type EqEqEq<X, Y> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? true : false;
type OF4 = EqEqEq<{ (x: 0, y: null): void; (x: number, y: null): void }, { (x: number, y: null): void; (x: 0, y: null): void }> // false But function type intersection does not work: type OF2 = EqEqEq<F & G, G & F> // true |
Can we re-open this or something? Because there isn't a way =( |
Seems like it's fixed in So, this doesn't need to be re-opened, I suppose. I forgot to link to that comment sooner, my bad. |
where can I find the infomations about the internal 'isTypeIdenticalTo' check? I can't find anything in the typescript official website.... |
@ldqUndefined I do not remember it well, but you may find it in the source code (or not, since TypeScript source code changed a lot). |
not work for:
|
I devised a type which seems to work for all scenarios, including intersection types. While it lacks the elegance of the other solutions, it appears to perform better than either based on the tests. type Equals<A, B> = _HalfEquals<A, B> extends true ? _HalfEquals<B, A> : false;
type _HalfEquals<A, B> = (
A extends unknown
? (
B extends unknown
? A extends B
? B extends A
? keyof A extends keyof B
? keyof B extends keyof A
? A extends object
? _DeepHalfEquals<A, B, keyof A> extends true
? 1
: never
: 1
: never
: never
: never
: never
: unknown
) extends never
? 0
: never
: unknown
) extends never
? true
: false;
type _DeepHalfEquals<A, B extends A, K extends keyof A> = (
K extends unknown ? (Equals<A[K], B[K]> extends true ? never : 0) : unknown
) extends never
? true
: false; Here is a TypeScript Playground link demonstrating the functionality on all the test cases. If anyone knows how to optimize this code without sacrificing functionality, I'd love to hear. |
@wvanvugt-speedline assertNotType<Equals<[any, number], [number, any]>>(); Here's mine (also fails there): type And<X,Y> = X extends true ? Y extends true ? true : false : false
type IsAny<T> = 0 extends (1 & T) ? true : false
type _Equals<X,Y> = (X extends Y ? 1 : 2) extends (Y extends X ? 1 : 3) ? true : false
type _EqualTuple<X,Y,Z> = X extends [any] ? Y extends [any] ? Equals<X[number], Y[number]> : false : Z
type Equals<X,Y> = _EqualTuple<X,Y, And<_Equals<IsAny<X>, IsAny<Y>>, _Equals<X,Y>>> |
I found this in
But I'm still not very clear what the related mean here? I can't understand the src code of |
I write some simple tests, seems type Foo<X> = <T>() => T extends X ? 1 : 2
type Bar<Y> = <T>() => T extends Y ? number : number
type Related = Foo<number> extends Bar<number> ? true : false // true
type UnRelated = Bar<number> extends Foo<number> ? true : false // false |
`Equals` and `assertType` are inspired by, respectively, microsoft/TypeScript#27024 (comment) and microsoft/TypeScript#27024. We carry on using standard ReactFlow edges and handles, for now. Though we may do something similar there eventually. EDIT: actually, we now define `PrimerEdge` for sort of forwards-compatibility This also puts us in slightly better shape to ditch ReactFlow for an alternative, should we want to, since we now implement better abstractions in some cases ourselves, using ReactFlow's API less extensively thoughout the codebase
As described in source comments, this is similar to ReactFlow's `Node`, but more type-safe. We are able to statically assert that the `data` fields of our nodes match what we tell ReactFlow to expect. `Equals` and `assertType` are inspired by, respectively, microsoft/TypeScript#27024 (comment) and microsoft/TypeScript#27024. We carry on using standard ReactFlow edges and handles, for now. Though we will likely do something similar there in future, once we require further customization. In fact, we now define a very simple `PrimerEdge` type for a small degree of forwards-compatibility. This also puts us in slightly better shape to ditch ReactFlow for an alternative, should we wish to, since we now implement better abstractions in some ways ourselves, using ReactFlow's types less extensively.
As described in source comments, this is similar to ReactFlow's `Node`, but more type-safe. We are able to statically assert that the `data` fields of our nodes match what we tell ReactFlow to expect. `Equals` and `assertType` are inspired by, respectively, microsoft/TypeScript#27024 (comment) and microsoft/TypeScript#27024. We carry on using standard ReactFlow edges and handles, for now. Though we will likely do something similar there in future, once we require further customization. In fact, we now define a very simple `PrimerEdge` type for a small degree of forwards-compatibility. This also puts us in slightly better shape to ditch ReactFlow for an alternative, should we wish to, since we now implement better abstractions in some ways ourselves, using ReactFlow's types less extensively.
`boolean` is effectively the union type `true | false` but the current `isMatch` implementation will treat overlapping union types as matching. This patch introduces a more strict type equivalence check based on microsoft/TypeScript#27024 (comment) and uses it when checking types that extend `boolean` for equivalence to `boolean`. The test added in this patch fails without the corresponding code changes here. Fixes #754.
The "type-level equality operator" should be incorporated as a standard type function in TypeScript, as it constitutes a fundamental aspect of type computations. Numerous valuable suggestions have been made in the comments, particularly "Matt's Equals<X, Y>," which works effectively in most cases, though it is not flawless. While some alternatives may outperform Matt's implementation, they tend to be overly complex. Therefore, I advocate for integrating this functionality directly into the TypeScript compiler. |
Hi, I mentioned in another thread, but not in this one. Feel free to check it out: |
…xclusively (#21) Fixes #29 Fixes #26 Fixes #5 > Note: I extracted a very small part of this PR to #20 This is a breaking change as I opted to remove the types that were no longer needed. They are exported though so it's likely some people depend on them. I can add these back as desired. This took a lot of tinkering. This topic and this equality check is discussed extensively at microsoft/TypeScript#27024 The main three edge-cases this implementation worked around are: 1. Explicitly handling `any` separately 2. Supporting identity unions 3. Supporting identity intersections The only remaining known issue with this implementation is: ```ts // @ts-expect-error This is the bug. expectTypeOf<{foo: number} & {bar: string}>().toEqualTypeOf<{foo: number; bar: string}>() ``` @shicks and I could not find a tweak to the `Equality` check to make this work. Instead, I added a workaround in the shape of a new `.simplified` modifier that works similar to `.not`: ```ts // The workaround is the new optional .simplified modifier. expectTypeOf<{foo: number} & {bar: string}>().simplified.toEqualTypeOf<{foo: number; bar: string}>() ``` I'm not entirely sure what to do with documenting `.simplified` because it's something you should never use unless you need it. The simplify operation tends to lose information about the types being tested (e.g., functions become `{}` and classes lose their constructors). I'll definitely update this PR to reference the `.simplified` modifier but I wanted to get a review on this approach first. One option would be to keep around all the `DeepBrand` stuff and to have `.deepBranded` or something being the modifier instead. That would have the benefit of preserving all the exported types making this less of a breaking change. --------- Co-authored-by: Misha Kaletsky <[email protected]> Co-authored-by: Misha Kaletsky <[email protected]>
x1 and x2, y1 and y2 must keep the identical types, otherwise the following problems will occur: type Foo<X> = <T>() => T extends X ? 1 : 2
type Bar<Y> = <T>() => T extends Y ? number : number
type Related = Foo<string> extends Bar<boolean> ? true : false // true |
@Mario-Marion feel free to check out or use the implementation in |
useSyncRef with two boolean needed a transform. Updated Equal type to properly compare boolean See: microsoft/TypeScript#27024 (comment)
|
It seems like saying
but in set theory that is the same as
The reason they are different lies in the implementation details only. I suppose some time saving shortcuts taken with Of course the more complicated version looks as unintelligible as Latin, so Geeks fluent in Latin love it. |
Search Terms
Suggestion
Use Cases
TypeScript type system is highly functional.
Type level testing is required.
However, we can not easily check type equivalence.
I want a type-level equivalence operator there.
It is difficult for users to implement any when they enter.
I implemented it, but I felt it was difficult to judge the equivalence of types including any.
Examples
Checklist
My suggestion meets these guidelines:
The text was updated successfully, but these errors were encountered: