-
-
Notifications
You must be signed in to change notification settings - Fork 730
Expand file tree
/
Copy pathrename-keys.d.ts
More file actions
163 lines (128 loc) · 5.85 KB
/
Copy pathrename-keys.d.ts
File metadata and controls
163 lines (128 loc) · 5.85 KB
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import type {IsLiteral} from './is-literal.d.ts';
import type {ReadonlyKeysOf} from './readonly-keys-of.d.ts';
import type {RequiredKeysOf} from './required-keys-of.d.ts';
import type {OptionalKeysOf} from './optional-keys-of.d.ts';
import type {OmitIndexSignature} from './omit-index-signature.d.ts';
import type {SetRequired} from './set-required.d.ts';
import type {SetReadonly} from './set-readonly.d.ts';
import type {IfNotAnyOrNever, IsExactOptionalPropertyTypesEnabled} from './internal/type.d.ts';
/**
Rename keys in an object type according to a map of old-to-new names.
@example
```
import type {RenameKeys} from 'type-fest';
type User = {
id: string;
firstName: string;
createdAt: Date;
};
type Renamed = RenameKeys<User, {firstName: 'first_name'; createdAt: 'created_at'}>;
//=> {id: string; first_name: string; created_at: Date}
```
@example
```
import type {RenameKeys} from 'type-fest';
type SearchInput = {
textQuery: string;
voiceQuery: Blob;
imageQuery: File;
};
type Normalized = RenameKeys<SearchInput, {textQuery: 'query'; voiceQuery: 'query'; imageQuery: 'query'}>;
//=> {query: string | Blob | File}
```
Note: When multiple source keys map to the same target, the target's value type is the union of the contributors' value types. The target is optional only when every contributor is optional, and is `readonly` when any contributor is `readonly`. With `exactOptionalPropertyTypes` disabled, the value type of a mixed-optionality merge also includes `undefined`.
@example
```
import type {RenameKeys} from 'type-fest';
// All colliding keys are required, so the target is required.
type A = RenameKeys<{a: 1; b: 2}, {a: 'x'; b: 'x'}>;
//=> {x: 1 | 2}
// All colliding keys are optional, so the target is optional.
type B = RenameKeys<{a?: 1; b?: 2}, {a: 'x'; b: 'x'}>;
//=> {x?: 1 | 2}
// One of the colliding keys is required, so the target is required.
type C = RenameKeys<{a: 1; b?: 2}, {a: 'x'; b: 'x'}>;
//=> {x: 1 | 2}
// One of the colliding keys is `readonly`, so the target is `readonly`.
type D = RenameKeys<{readonly a: 1; b: 2}, {a: 'x'; b: 'x'}>;
//=> {readonly x: 1 | 2}
```
@example
```
// @exactOptionalPropertyTypes: false
import type {RenameKeys} from 'type-fest';
// With `exactOptionalPropertyTypes` disabled, a mixed-optionality merge includes `undefined`.
type E = RenameKeys<{a?: 1; b: 2}, {a: 'x'; b: 'x'}>;
//=> {x: 1 | 2 | undefined}
```
Note: A union target distributes, producing one output key per member.
@example
```
import type {RenameKeys} from 'type-fest';
type A = RenameKeys<{a: string}, {a: 'b' | 'c'}>;
//=> {b: string; c: string}
```
Note: An entry whose value is not a literal `PropertyKey` (such as `string`) is also ignored, leaving that key's name unchanged.
@example
```
import type {RenameKeys} from 'type-fest';
type A = RenameKeys<{a: 1}, {a: string}>;
//=> {a: 1}
type B = RenameKeys<{a: 1; b: 2}, {a: 'x'; b: symbol}>;
//=> {x: 1; b: 2}
```
Note: A rename map entry whose key is not a property of the source type is ignored.
@example
```
import type {RenameKeys} from 'type-fest';
type A = RenameKeys<{a: 1; b: 2}, {a: 'x'; c: 'y'}>;
//=> {x: 1; b: 2}
```
@category Object
*/
export type RenameKeys<
BaseType extends object,
RenameMap extends Record<PropertyKey, PropertyKey>,
> = IfNotAnyOrNever<BaseType, {
ifNot: BaseType extends unknown // For distributing `BaseType`
? RenameMap extends unknown // For distributing `RenameMap`
? RenameOnce<BaseType, NormalizeMap<RenameMap>>
: never
: never;
}>;
type RenameOnce<BaseType extends object, RenameMap extends Record<PropertyKey, PropertyKey>> =
RestoreMergedUndefined<BaseType, RenameMap,
ApplyReadonly<BaseType, RenameMap,
ApplyRequired<BaseType, RenameMap,
RenameNaive<BaseType, RenameMap>>>>;
type RenameNaive<BaseType extends object, RenameMap extends Record<PropertyKey, PropertyKey>> = {
// Two keys mapping to one target produce a union value and keep only the first key's modifiers.
// Like for example, `{a?: 1; b: 2}` with `{a: 'x'; b: 'x'}` produces `{x?: 1 | 2}`, taking `a`'s optional.
[Key in keyof BaseType as TargetOf<Key, RenameMap>]: Required<BaseType>[Key];
};
// A merged target kept only one modifier in `RenameNaive`, so re-force these from every contributor.
type ApplyRequired<BaseType extends object, RenameMap extends Record<PropertyKey, PropertyKey>, Renamed> =
SetRequired<Renamed, TargetOf<RequiredKeysOf<OmitIndexSignature<BaseType>>, RenameMap> & keyof Renamed>;
type ApplyReadonly<BaseType extends object, RenameMap extends Record<PropertyKey, PropertyKey>, Renamed> =
SetReadonly<Renamed, TargetOf<ReadonlyKeysOf<OmitIndexSignature<BaseType>>, RenameMap> & keyof Renamed>;
// `exactOptionalPropertyTypes` off keeps `undefined` on a target that merged a required and an optional source.
// Source `{a?: 1; x: 2}` with `{a: 'x'}` gives `{x: 1 | 2 | undefined}`.
type RestoreMergedUndefined<BaseType extends object, RenameMap extends Record<PropertyKey, PropertyKey>, Result> =
IsExactOptionalPropertyTypesEnabled extends true
? Result
: {
[Key in keyof Result]: Key extends MergedTargets<BaseType, RenameMap>
? Result[Key] | undefined
: Result[Key];
};
type MergedTargets<BaseType extends object, RenameMap extends Record<PropertyKey, PropertyKey>> =
// Targets renamed from both a required and an optional key prefer the required modifier.
// Required `b` and optional `a` both rename to `x` in `{a?: 1; b: 2}` with `{a: 'x'; b: 'x'}`.
TargetOf<RequiredKeysOf<OmitIndexSignature<BaseType>>, RenameMap>
& TargetOf<OptionalKeysOf<OmitIndexSignature<BaseType>>, RenameMap>;
type NormalizeMap<RenameMap extends Record<PropertyKey, PropertyKey>> = {
-readonly [Key in keyof RenameMap as true extends IsLiteral<RenameMap[Key]> ? Key : never]-?: RenameMap[Key];
};
type TargetOf<SourceKey extends PropertyKey, RenameMap extends Record<PropertyKey, PropertyKey>> =
SourceKey extends keyof RenameMap ? RenameMap[SourceKey] : SourceKey;
export {};