Skip to content

Commit d246577

Browse files
authored
add: Xor type
1 parent 785549f commit d246577

File tree

2 files changed

+127
-0
lines changed

2 files changed

+127
-0
lines changed

source/xor.d.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
2+
/**
3+
Returns `false` if the giving type is `never`, else return it.
4+
*/
5+
export type IfNever<T> = [T] extends [never] ? false : T;
6+
7+
/**
8+
Returns a boolean for whether either of two given types are true.
9+
10+
Use-case: Constructing complex conditional types where multiple conditions must be satisfied.
11+
12+
@example
13+
```
14+
import type {Xor} from 'type-fest';
15+
16+
type TT = Xor<true, false>;
17+
//=> true
18+
19+
type TF = Xor<true, false>;
20+
//=> true
21+
22+
type FT = Xor<false, true>;
23+
//=> true
24+
25+
type FF = Xor<false, false>;
26+
//=> false
27+
```
28+
29+
Note: When `boolean` is passed as an argument, it is distributed into separate cases, and the final result is a union of those cases.
30+
For example, `Xor<false, boolean>` expands to `Xor<false, true> | Xor<false, false>`, which simplifies to `true | false` (i.e., `boolean`).
31+
@example
32+
```
33+
import type {Xor} from 'type-fest';
34+
35+
type A = Xor<false, boolean>;
36+
//=> boolean
37+
38+
type B = Xor<boolean, false>;
39+
//=> boolean
40+
41+
type C = Xor<true, boolean>;
42+
//=> true
43+
44+
type D = Xor<boolean, true>;
45+
//=> true
46+
47+
type E = Xor<boolean, boolean>;
48+
//=> boolean
49+
```
50+
51+
Note: If `never` is passed as an argument, it is treated as `false` and the result is computed accordingly.
52+
53+
@example
54+
```
55+
import type {Xor} from 'type-fest';
56+
57+
type A = Xor<true, never>;
58+
//=> true
59+
60+
type B = Xor<never, true>;
61+
//=> true
62+
63+
type C = Xor<false, never>;
64+
//=> false
65+
66+
type D = Xor<never, false>;
67+
//=> false
68+
69+
type E = Xor<boolean, never>;
70+
//=> boolean
71+
72+
type F = Xor<never, boolean>;
73+
//=> boolean
74+
75+
type G = Xor<never, never>;
76+
//=> false
77+
```
78+
79+
@see And, Or
80+
*/
81+
export type Xor<A extends boolean, B extends boolean> = _Xor<IfNever<A>, IfNever<B>>; // `never` is treated as `false`
82+
83+
export type _Xor<A extends boolean, B extends boolean> =
84+
A extends true
85+
? B extends true
86+
? false
87+
: true
88+
: B extends true
89+
? true
90+
: false;
91+
92+
type T = Xor<never, never>
93+
// ^?
94+
95+
export {};

test-d/xor.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import {expectType} from 'tsd';
2+
import type {Xor} from '../source/xor.d.ts';
3+
4+
declare const boolean: boolean;
5+
6+
expectType<Xor<true, true>>(false);
7+
expectType<Xor<true, false>>(true);
8+
expectType<Xor<false, true>>(true);
9+
expectType<Xor<false, false>>(false);
10+
11+
expectType<Xor<true, boolean>>(boolean);
12+
expectType<Xor<boolean, true>>(boolean);
13+
expectType<Xor<false, boolean>>(boolean);
14+
expectType<Xor<boolean, false>>(boolean);
15+
expectType<Xor<boolean, boolean>>(boolean);
16+
17+
// Boundary cases
18+
expectType<Xor<true, any>>(boolean);
19+
expectType<Xor<any, true>>(boolean);
20+
expectType<Xor<false, any>>(boolean);
21+
expectType<Xor<any, false>>(boolean);
22+
expectType<Xor<boolean, any>>(boolean);
23+
expectType<Xor<any, boolean>>(boolean);
24+
expectType<Xor<any, any>>(boolean);
25+
26+
expectType<Xor<true, never>>(true);
27+
expectType<Xor<never, true>>(true);
28+
expectType<Xor<false, never>>(false);
29+
expectType<Xor<never, false>>(false);
30+
expectType<Xor<boolean, never>>(boolean);
31+
expectType<Xor<never, boolean>>(boolean);
32+
expectType<Xor<never, never>>(false);

0 commit comments

Comments
 (0)