-
-
Notifications
You must be signed in to change notification settings - Fork 545
/
distributed-omit.d.ts
89 lines (67 loc) · 2.2 KB
/
distributed-omit.d.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import type {KeysOfUnion} from './keys-of-union';
/**
Omits keys from a type, distributing the operation over a union.
TypeScript's `Omit` doesn't distribute over unions, leading to the erasure of unique properties from union members when omitting keys. This creates a type that only retains properties common to all union members, making it impossible to access member-specific properties after the Omit. Essentially, using `Omit` on a union type merges the types into a less specific one, hindering type narrowing and property access based on discriminants. This type solves that.
Example:
```
type A = {
discriminant: 'A';
foo: string;
a: number;
};
type B = {
discriminant: 'B';
foo: string;
b: string;
};
type Union = A | B;
type OmittedUnion = Omit<Union, 'foo'>;
//=> {discriminant: 'A' | 'B'}
const omittedUnion: OmittedUnion = createOmittedUnion();
if (omittedUnion.discriminant === 'A') {
// We would like to narrow `omittedUnion`'s type
// to `A` here, but we can't because `Omit`
// doesn't distribute over unions.
omittedUnion.a;
//=> Error: `a` is not a property of `{discriminant: 'A' | 'B'}`
}
```
While `Except` solves this problem, it restricts the keys you can omit to the ones that are present in **ALL** union members, where `DistributedOmit` allows you to omit keys that are present in **ANY** union member.
@example
```
type A = {
discriminant: 'A';
foo: string;
a: number;
};
type B = {
discriminant: 'B';
foo: string;
bar: string;
b: string;
};
type C = {
discriminant: 'C';
bar: string;
c: boolean;
};
// Notice that `foo` exists in `A` and `B`, but not in `C`, and
// `bar` exists in `B` and `C`, but not in `A`.
type Union = A | B | C;
type OmittedUnion = DistributedOmit<Union, 'foo' | 'bar'>;
const omittedUnion: OmittedUnion = createOmittedUnion();
if (omittedUnion.discriminant === 'A') {
omittedUnion.a;
//=> OK
omittedUnion.foo;
//=> Error: `foo` is not a property of `{discriminant: 'A'; a: string}`
omittedUnion.bar;
//=> Error: `bar` is not a property of `{discriminant: 'A'; a: string}`
}
```
@category Object
*/
export type DistributedOmit<ObjectType, KeyType extends KeysOfUnion<ObjectType>> =
ObjectType extends unknown
? Omit<ObjectType, KeyType>
: never;