Skip to content

Commit

Permalink
fix: Equal<A,B> with union types
Browse files Browse the repository at this point in the history
The original implementation from `tyeppark` does not work with union type.
  • Loading branch information
unional committed Sep 12, 2022
1 parent 7c6e5aa commit 4ad548a
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 16 deletions.
13 changes: 11 additions & 2 deletions ts/predicates/Equal.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { assertType, Equal, NotEqual } from '../index.js'

describe('TypeEquals', () => {
describe('Equal<A, B>', () => {
test('match', () => {
assertType.isTrue(true as Equal<false, false>)
})
Expand Down Expand Up @@ -54,9 +54,14 @@ describe('TypeEquals', () => {
test('overlap is false', () => {
assertType.isFalse(false as Equal<{ a: 1, b: 1 }, { a: 1, c: 2 }>)
})

it('works with union types', () => {
assertType.isTrue(true as Equal<{ a: number, b: string }, { a: number } & { b: string }>)
assertType.isTrue(true as Equal<{ a: number, b?: string }, { a: number } & { b?: string }>)
})
})

describe('TypeNotEquals', () => {
describe('NotEqual<A, B>', () => {
test('boolean', () => {
assertType.isFalse(false as NotEqual<boolean, boolean>)
assertType.isFalse(false as NotEqual<true, true>)
Expand All @@ -68,4 +73,8 @@ describe('TypeNotEquals', () => {
assertType.isTrue(true as NotEqual<false, true>)
assertType.isTrue(true as NotEqual<true, false>)
})
it('works with union types', () => {
assertType.isFalse(false as NotEqual<{ a: number, b: string }, { a: number } & { b: string }>)
assertType.isTrue(true as NotEqual<{ a: number, b: string }, { a: number } & { b?: string }>)
})
})
37 changes: 23 additions & 14 deletions ts/predicates/Equal.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
/**
* Checks if two types are equal.
* Borrow from `typepark`.
* The simple `A extends B ? B extends A ? true : false : false`
* does not work with boolean type.
*/
export type Equal<A, B, Then = true, Else = false> =
(<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2)
? Then : Else
export type IsEqual<A, B> = Equal<A, B>

export type NotEqual<A, B, Then = true, Else = false> =
(<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? Else : Then
export type IsNotEqual<A, B> = NotEqual<A, B>
/**
* `<T>() => T extends A ? 1 : 2` idea originate from `typepark`
* But it does not work with union types.
*
* `<T>() => T extends A` is a trick to create an inferred type `T`.
* This is needed for `boolean`, `string`, and `number`,
* as they supports literal types.
*/

/**
* Checks if two types are equal.
*/
export type Equal<A, B, Then = true, Else = false> =
[A, B] extends [object, object]
? (A extends B ? B extends A ? Then : Else : Else)
: (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? Then : Else
export type IsEqual<A, B> = Equal<A, B>

export type NotEqual<A, B, Then = true, Else = false> =
[A, B] extends [object, object]
? (A extends B ? B extends A ? Else : Then : Then)
: (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? Else : Then
export type IsNotEqual<A, B> = NotEqual<A, B>

0 comments on commit 4ad548a

Please sign in to comment.