Skip to content

Commit e4bd1e8

Browse files
update: remove SimpleIsEqual, move LastOfUnion to internal, delete unused tests
1 parent 614ad2a commit e4bd1e8

File tree

12 files changed

+91
-287
lines changed

12 files changed

+91
-287
lines changed

index.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,6 @@ export type {IsNullable} from './source/is-nullable.d.ts';
167167
export type {TupleOf} from './source/tuple-of.d.ts';
168168
export type {ExclusifyUnion} from './source/exclusify-union.d.ts';
169169
export type {ArrayReverse} from './source/array-reverse.d.ts';
170-
export type {LastOfUnion} from './source/last-of-union.d.ts';
171170

172171
// Template literal types
173172
export type {CamelCase, CamelCaseOptions} from './source/camel-case.d.ts';

readme.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,6 @@ Click the type names for complete docs.
191191
- [`ConditionalSimplify`](source/conditional-simplify.d.ts) - Simplifies a type while including and/or excluding certain types from being simplified.
192192
- [`ConditionalSimplifyDeep`](source/conditional-simplify-deep.d.ts) - Recursively simplifies a type while including and/or excluding certain types from being simplified.
193193
- [`ExclusifyUnion`](source/exclusify-union.d.ts) - Ensure mutual exclusivity in object unions by adding other members’ keys as `?: never`.
194-
- [`LastOfUnion`](source/last-of-union.d.ts) - Return a member of a union type. Order is not guaranteed.
195194

196195
### Type Guard
197196

source/exclude-exactly.d.ts

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,30 @@
11
import type {IsNever} from './is-never.d.ts';
22
import type {IsAny} from './is-any.d.ts';
33
import type {If} from './if.d.ts';
4-
import type {IfNotAnyOrNever, SimpleIsEqual} from './internal/type.d.ts';
4+
import type {IsEqual} from './is-equal.d.ts';
5+
import type {IfNotAnyOrNever} from './internal/type.d.ts';
56

67
/**
7-
A stricter version of `Exclude<T, U>` that ensures objects with different key modifiers are not considered identical.
8-
9-
TypeScript's built-in `Exclude` and `ExcludeStrict` in `type-fest` don't distinguish key modifiers of objects.
10-
11-
@example
12-
```
13-
import type {ExcludeStrict} from 'type-fest';
14-
15-
type NeverReturned_0 = Exclude<{a: 0} | {readonly a: 0}, {readonly a: 0}>;
16-
//=> never
17-
type NeverReturned_1 = ExcludeStrict<{a: 0} | {readonly a: 0}, {readonly a: 0}>;
18-
//=> never
19-
```
8+
A stricter version of `Exclude<T, U>` that excludes types only when they are exactly identical.
209
2110
`ExcludeExactly` keeps the union members element if the members are not identical.
2211
2312
@example
2413
```
2514
import type {ExcludeExactly} from 'type-fest';
2615
27-
type ExcludeNever = ExcludeExactly<{a: 0} | {a: 0} | {readonly a: 0}, never>;
28-
//=> {a: 0} | {a: 0} | {readonly a: 0}
29-
type ExcludeReadonlyKey = ExcludeExactly<{a: 0} | {readonly a: 0}, {readonly a: 0}>;
30-
//=> {a: 0}
31-
type ExcludeKey = ExcludeExactly<{readonly a: 0}, {a: 0}>;
32-
//=> {readonly a: 0}
33-
type ExcludeReadonly = ExcludeExactly<{readonly a: 0}, {readonly a: 0}>;
16+
type TestExclude1 = Exclude<'a' | 'b' | 'c' | 1 | 2 | 3, string>;
17+
//=> 1 | 2 | 3
18+
type TestExcludeExactly1 = ExcludeExactly<'a' | 'b' | 'c' | 1 | 2 | 3, string>;
19+
//=> 'a' | 'b' | 'c' | 1 | 2 | 3
20+
type TestExclude2 = Exclude<'a' | 'b' | 'c' | 1 | 2 | 3, any>;
3421
//=> never
35-
type ExcludeSubType = ExcludeExactly<0 | 1 | number, 1>;
36-
//=> number
37-
type ExcludeAllSet = ExcludeExactly<0 | 1 | number, number>;
22+
type TestExcludeExactly2 = ExcludeExactly<'a' | 'b' | 'c' | 1 | 2 | 3, any>;
23+
//=> 'a' | 'b' | 'c' | 1 | 2 | 3
24+
type TestExclude3 = Exclude<{a: string} | {a: string; b: string}, {a: string}>;
3825
//=> never
39-
type ExcludeFromUnknown = ExcludeExactly<unknown, string>;
40-
//=> unknown
41-
type ExcludeFromUnknownArray = ExcludeExactly<number[] | unknown[], number[]>;
42-
//=> unknown[]
26+
type TestExcludeExactly3 = ExcludeExactly<{a: string} | {a: string; b: string}, {a: string}>;
27+
//=> {a: string; b: string}
4328
```
4429
4530
@category Improved Built-in
@@ -58,7 +43,7 @@ type _ExcludeExactly<Union, Delete> =
5843
IfNotAnyOrNever<Delete,
5944
Union extends unknown // For distributing `Union`
6045
? [Delete extends unknown // For distributing `Delete`
61-
? If<SimpleIsEqual<Union, Delete>, true, never>
46+
? If<IsEqual<Union, Delete>, true, never>
6247
: never] extends [never] ? Union : never
6348
: never,
6449
// If `Delete` is `any` or `never`, then return `Union`,

source/internal/type.d.ts

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type {IsAny} from '../is-any.d.ts';
33
import type {IsNever} from '../is-never.d.ts';
44
import type {Primitive} from '../primitive.d.ts';
55
import type {UnknownArray} from '../unknown-array.d.ts';
6+
import type {UnionToIntersection} from '../union-to-intersection.d.ts';
67

78
/**
89
Matches any primitive, `void`, `Date`, or `RegExp` value.
@@ -162,27 +163,45 @@ export type IsExactOptionalPropertyTypesEnabled = [(string | undefined)?] extend
162163
: true;
163164

164165
/**
165-
A simple version of `IsEqual`.
166+
Return a member of a union type. Order is not guaranteed.
167+
Returns `never` when the input is `never`.
166168
167-
`SimpleIsEqual<never, unknown>` and `SimpleIsEqual<unknown, never>` return `true`, whereas `IsEqual` returns `false` correctly.
169+
@see https://github.com/microsoft/TypeScript/issues/13298#issuecomment-468375328
168170
169-
`SimpleIsEqual` doesn't return `false` correctly for identical union/intersection arguments.
171+
Use-cases:
172+
- Implementing recursive type functions that accept a union type.
173+
- Reducing a union one member at a time, for example when building tuples.
174+
175+
It can detect a termination case using {@link IsNever `IsNever`}.
170176
171177
@example
172178
```
173-
type UnionCase = SimpleIsEqual<{a: {b: 0} | {b: 0}}, {a: {b: 0}}>;
174-
//=> false
179+
import type {LastOfUnion, ExcludeExactly, IsNever} from 'type-fest';
175180
176-
type IntersectionCase = SimpleIsEqual<{a: {b: 0} & {b: 0}}, {a: {b: 0}}>;
177-
//=> false
181+
export type UnionToTuple<T, L = LastOfUnion<T>> =
182+
IsNever<T> extends false
183+
? [...UnionToTuple<ExcludeExactly<T, L>>, L]
184+
: [];
185+
```
186+
187+
@example
178188
```
189+
import type {LastOfUnion} from 'type-fest';
179190
180-
`SimpleIsEqual` fails the `equalWrappedTupleIntersectionToBeNeverAndNeverExpanded` test in `test-d/internal/simple-is-equal.ts`.
191+
type Last = LastOfUnion<1 | 2 | 3>;
192+
//=> 3
193+
194+
type LastNever = LastOfUnion<never>;
195+
//=> never
196+
```
197+
198+
@category Type
181199
*/
182-
export type SimpleIsEqual<A, B> =
183-
(<G>() => G extends A & G | G ? 1 : 2) extends
184-
(<G>() => G extends B & G | G ? 1 : 2)
185-
? true
186-
: false;
200+
export type LastOfUnion<T> =
201+
true extends IsNever<T>
202+
? never
203+
: UnionToIntersection<T extends any ? () => T : never> extends () => (infer R)
204+
? R
205+
: never;
187206

188207
export {};

source/is-equal.d.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import type {SimpleIsEqual} from './internal/type.d.ts';
2-
31
/**
42
Returns a boolean for whether the two given types are equal.
53
@@ -29,8 +27,14 @@ type Includes<Value extends readonly any[], Item> =
2927
export type IsEqual<A, B> =
3028
[A] extends [B]
3129
? [B] extends [A]
32-
? SimpleIsEqual<A, B>
30+
? _IsEqual<A, B>
3331
: false
3432
: false;
3533

34+
type _IsEqual<A, B> =
35+
(<G>() => G extends A & G | G ? 1 : 2) extends
36+
(<G>() => G extends B & G | G ? 1 : 2)
37+
? true
38+
: false;
39+
3640
export {};

source/last-of-union.d.ts

Lines changed: 0 additions & 46 deletions
This file was deleted.

source/union-to-tuple.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type {IsNever} from './is-never.d.ts';
22
import type {ExcludeExactly} from './exclude-exactly.d.ts';
3-
import type {LastOfUnion} from './last-of-union.d.ts';
3+
import type {LastOfUnion} from './internal/index.d.ts';
44

55
/**
66
Convert a union type into an unordered tuple type of its elements.

test-d/exclude-exactly.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import {expectType} from 'tsd';
2-
import type {ExcludeExactly} from '../index.d.ts';
2+
import type {LastOfUnion} from '../source/internal/index.d.ts';
3+
import type {ExcludeExactly, IsNever} from '../index.d.ts';
34

4-
expectType<number>({} as ExcludeExactly<0 | 1 | number, '1'>);
5-
expectType<never>({} as ExcludeExactly<0 | 1 | number, number>);
5+
expectType<number>({} as ExcludeExactly<number, '1'>);
6+
expectType<never>({} as ExcludeExactly<number, number>);
67
expectType<0>({} as ExcludeExactly<0, number>);
7-
expectType<string>({} as ExcludeExactly<'0' | '1' | string, '1'>);
8-
expectType<never>({} as ExcludeExactly<'0' | '1' | string, string>);
8+
expectType<string>({} as ExcludeExactly<string, '1'>);
9+
expectType<never>({} as ExcludeExactly<string, string>);
910

1011
// `{readonly a: t}` should not be equal to `{a: t}` because of assignability.
1112
expectType<{a: 0}>({} as ExcludeExactly<{a: 0} | {readonly a: 0}, {readonly a: 0}>);

test-d/internal/last-of-union.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import {expectType} from 'tsd';
2+
import type {LastOfUnion} from '../../source/internal/index.d.ts';
3+
import type {IsAny, IsUnknown, UnionToTuple} from '../../index.d.ts';
4+
5+
// `LastOfUnion` distinguishes between different modifiers.
6+
type UnionType = {a: 0} | {b: 0} | {a?: 0} | {readonly a?: 0} | {readonly a: 0};
7+
expectType<true>({} as LastOfUnion<UnionType> extends UnionType ? true : false);
8+
expectType<false>({} as UnionType extends LastOfUnion<UnionType> ? true : false);
9+
10+
// `never` acts as a termination condition with `IsNever`.
11+
expectType<never>({} as LastOfUnion<never>);
12+
13+
expectType<true>({} as IsUnknown<LastOfUnion<unknown>>);
14+
expectType<true>({} as IsAny<LastOfUnion<any>>);
15+
16+
type DifferentModifierUnion = {readonly a: 0} | {a: 0};
17+
expectType<DifferentModifierUnion>({} as UnionToTuple<DifferentModifierUnion>[number]);
18+
expectType<2>({} as UnionToTuple<DifferentModifierUnion>['length']);
19+
20+
type ReversedDifferentModifierUnion = {a: 0} | {readonly a: 0};
21+
expectType<ReversedDifferentModifierUnion>({} as UnionToTuple<ReversedDifferentModifierUnion>[number]);
22+
expectType<2>({} as UnionToTuple<ReversedDifferentModifierUnion>['length']);

test-d/internal/simple-is-equal.ts

Lines changed: 0 additions & 95 deletions
This file was deleted.

0 commit comments

Comments
 (0)