UNPKG

2.08 kBTypeScriptView Raw
1import type {KeysOfUnion} from './keys-of-union';
2
3/**
4Pick keys from a type, distributing the operation over a union.
5
6TypeScript's `Pick` doesn't distribute over unions, leading to the erasure of unique properties from union members when picking keys. This creates a type that only retains properties common to all union members, making it impossible to access member-specific properties after the Pick. Essentially, using `Pick` 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.
7
8Example:
9
10```
11type A = {
12 discriminant: 'A';
13 foo: {
14 bar: string;
15 };
16};
17
18type B = {
19 discriminant: 'B';
20 foo: {
21 baz: string;
22 };
23};
24
25type Union = A | B;
26
27type PickedUnion = Pick<Union, 'discriminant' | 'foo'>;
28//=> {discriminant: 'A' | 'B', foo: {bar: string} | {baz: string}}
29
30const pickedUnion: PickedUnion = createPickedUnion();
31
32if (pickedUnion.discriminant === 'A') {
33 // We would like to narrow `pickedUnion`'s type
34 // to `A` here, but we can't because `Pick`
35 // doesn't distribute over unions.
36
37 pickedUnion.foo.bar;
38 //=> Error: Property 'bar' does not exist on type '{bar: string} | {baz: string}'.
39}
40```
41
42@example
43```
44type A = {
45 discriminant: 'A';
46 foo: {
47 bar: string;
48 };
49 extraneous: boolean;
50};
51
52type B = {
53 discriminant: 'B';
54 foo: {
55 baz: string;
56 };
57 extraneous: boolean;
58};
59
60// Notice that `foo.bar` exists in `A` but not in `B`.
61
62type Union = A | B;
63
64type PickedUnion = DistributedPick<Union, 'discriminant' | 'foo'>;
65
66const pickedUnion: PickedUnion = createPickedUnion();
67
68if (pickedUnion.discriminant === 'A') {
69 pickedUnion.foo.bar;
70 //=> OK
71
72 pickedUnion.extraneous;
73 //=> Error: Property `extraneous` does not exist on type `Pick<A, 'discriminant' | 'foo'>`.
74
75 pickedUnion.foo.baz;
76 //=> Error: `bar` is not a property of `{discriminant: 'A'; a: string}`.
77}
78```
79
80@category Object
81*/
82export type DistributedPick<ObjectType, KeyType extends KeysOfUnion<ObjectType>> =
83 ObjectType extends unknown
84 ? Pick<ObjectType, Extract<KeyType, keyof ObjectType>>
85 : never;