|
1 | | -import type {IsUnknown} from './is-unknown.d.ts'; |
2 | 1 | import type {IsNever} from './is-never.d.ts'; |
3 | 2 | import type {IsAny} from './is-any.d.ts'; |
4 | | -import type {LastOfUnion} from './last-of-union.d.ts'; |
5 | | - |
6 | | -/** |
7 | | -Return `never` if the first and second arguments are identical. |
8 | | -Return the first argument if not. |
9 | | -(But there's a limitation about union/intersection type. See `IsEqual` in `source/is-equal.d.ts`.) |
10 | | -
|
11 | | -@example |
12 | | -``` |
13 | | -type A = MatchOrNever<string | number, string>; |
14 | | -//=> string | number |
15 | | -type B = MatchOrNever<string | number, string | number>; |
16 | | -//=> never |
17 | | -type C = MatchOrNever<string | number, unknown>; |
18 | | -//=> string | number |
19 | | -type D = MatchOrNever<string, string | number>; |
20 | | -//=> string |
21 | | -``` |
22 | | -
|
23 | | -This does NOT depend on assignability. |
24 | | -
|
25 | | -@example |
26 | | -``` |
27 | | -type RO_0 = MatchOrNever<{readonly a: 0}, {a: 0}>; |
28 | | -//=> {readonly a: 0} |
29 | | -type RO_1 = MatchOrNever<{a: 0}, {readonly a: 0}>; |
30 | | -//=> {a: 0} |
31 | | -``` |
32 | | -
|
33 | | -`unknown` and `never` cases, which easily break equality in type-level codebase. |
34 | | -
|
35 | | -@example |
36 | | -``` |
37 | | -type E = MatchOrNever<unknown, never>; |
38 | | -//=> unknown |
39 | | -type F = MatchOrNever<unknown, unknown>; |
40 | | -//=> never |
41 | | -type G = MatchOrNever<never, never>; |
42 | | -//=> never |
43 | | -type H = MatchOrNever<never, unknown>; |
44 | | -//=> never |
45 | | -``` |
46 | | -
|
47 | | -Note that this doesn't regard the identical union/intersection type `T | T` and/or `T & T` as `T` recursively. |
48 | | -e.g., `{a: 0} | {a: 0}` and/or `{a: 0} & {a: 0}` as `{a: 0}`. |
49 | | -
|
50 | | -@example |
51 | | -``` |
52 | | -type IDUnion = MatchOrNever<{a: {b: 0}} | {a: {b: 0}}, {a: {b: 0}}>; |
53 | | -//=> never |
54 | | -type A = {a: {b: 0} | {b: 0}}; |
55 | | -type RecurivelyIDUnion = MatchOrNever<A, {a: {b: 0}}>; |
56 | | -//=> A |
57 | | -``` |
58 | | -*/ |
59 | | -type MatchOrNever<A, B> = |
60 | | - [unknown, B] extends [A, never] |
61 | | - ? A |
62 | | - // This equality code base below doesn't work if `A` is `unknown` and `B` is `never` case. |
63 | | - // So this branch should be wrapped to take care of this. |
64 | | - : (<G>() => G extends A & G | G ? 1 : 2) extends (<G>() => G extends B & G | G ? 1 : 2) |
65 | | - ? never |
66 | | - : A; |
| 3 | +import type {If} from './if.d.ts'; |
| 4 | +import type {IfNotAnyOrNever} from './internal/type.d.ts'; |
67 | 5 |
|
68 | 6 | /** |
69 | 7 | A stricter version of `Exclude<T, U>` that ensures objects with different key modifiers are not considered identical. |
@@ -106,19 +44,32 @@ type ExcludeFromUnknownArray = ExcludeExactly<number[] | unknown[], number[]>; |
106 | 44 |
|
107 | 45 | @category Improved Built-in |
108 | 46 | */ |
109 | | -export type ExcludeExactly<UnionU, DeleteT> = |
110 | | - LastOfUnion<DeleteT> extends infer D |
111 | | - ? true extends IsNever<D> |
112 | | - ? UnionU |
113 | | - : ExcludeExactly<_ExcludeExactly<UnionU, D>, _ExcludeExactly<DeleteT, D>> |
114 | | - : never; |
| 47 | +export type ExcludeExactly<Union, Delete> = |
| 48 | + IfNotAnyOrNever< |
| 49 | + Union, |
| 50 | + _ExcludeExactly<Union, Delete>, |
| 51 | + // If `Union` is `any`, then if `Delete` is `any`, return `never`, else return `Union`. |
| 52 | + If<IsAny<Delete>, never, Union>, |
| 53 | + // If `Union` is `never`, then if `Delete` is `never`, return `never`, else return `Union`. |
| 54 | + If<IsNever<Delete>, never, Union> |
| 55 | + >; |
| 56 | + |
| 57 | +type _ExcludeExactly<Union, Delete> = |
| 58 | + IfNotAnyOrNever<Delete, |
| 59 | + Union extends unknown // For distributing `Union` |
| 60 | + ? [Delete extends unknown // For distributing `Delete` |
| 61 | + ? If<SimpleIsEqual<Union, Delete>, true, never> |
| 62 | + : never] extends [never] ? Union : never |
| 63 | + : never, |
| 64 | + // If `Delete` is `any` or `never`, then return `Union`, |
| 65 | + // because `Union` cannot be `any` or `never` here. |
| 66 | + Union, Union |
| 67 | + >; |
| 68 | + |
| 69 | +type SimpleIsEqual<A, B> = |
| 70 | + (<G>() => G extends A & G | G ? 1 : 2) extends |
| 71 | + (<G>() => G extends B & G | G ? 1 : 2) |
| 72 | + ? true |
| 73 | + : false; |
115 | 74 |
|
116 | | -type _ExcludeExactly<UnionU, DeleteT> = |
117 | | - true extends IsAny<DeleteT> |
118 | | - ? never |
119 | | - : true extends IsUnknown<DeleteT> |
120 | | - ? never |
121 | | - : UnionU extends unknown // Only for union distribution. |
122 | | - ? MatchOrNever<UnionU, DeleteT> |
123 | | - : never; |
124 | 75 | export {}; |
0 commit comments