type-fest 0.6.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -7,9 +7,14 @@ export {Mutable} from './source/mutable';
7
7
  export {Merge} from './source/merge';
8
8
  export {MergeExclusive} from './source/merge-exclusive';
9
9
  export {RequireAtLeastOne} from './source/require-at-least-one';
10
+ export {RequireExactlyOne} from './source/require-exactly-one';
11
+ export {PartialDeep} from './source/partial-deep';
10
12
  export {ReadonlyDeep} from './source/readonly-deep';
11
13
  export {LiteralUnion} from './source/literal-union';
12
14
  export {Promisable} from './source/promisable';
15
+ export {Opaque} from './source/opaque';
16
+ export {SetOptional} from './source/set-optional';
17
+ export {SetRequired} from './source/set-required';
13
18
 
14
19
  // Miscellaneous
15
20
  export {PackageJson} from './source/package-json';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "type-fest",
3
- "version": "0.6.0",
3
+ "version": "0.8.1",
4
4
  "description": "A collection of essential TypeScript types",
5
5
  "license": "(MIT OR CC0-1.0)",
6
6
  "repository": "sindresorhus/type-fest",
@@ -32,9 +32,9 @@
32
32
  ],
33
33
  "devDependencies": {
34
34
  "@sindresorhus/tsconfig": "^0.4.0",
35
- "@typescript-eslint/eslint-plugin": "^1.9.0",
36
- "@typescript-eslint/parser": "^1.10.2",
37
- "eslint-config-xo-typescript": "^0.14.0",
35
+ "@typescript-eslint/eslint-plugin": "^2.2.0",
36
+ "@typescript-eslint/parser": "^2.2.0",
37
+ "eslint-config-xo-typescript": "^0.18.0",
38
38
  "tsd": "^0.7.3",
39
39
  "xo": "^0.24.0"
40
40
  },
package/readme.md CHANGED
@@ -65,13 +65,18 @@ Click the type names for complete docs.
65
65
  ### Utilities
66
66
 
67
67
  - [`Except`](source/except.d.ts) - Create a type from an object type without certain keys. This is a stricter version of [`Omit`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html#the-omit-helper-type).
68
- - [`Mutable`](source/mutable.d.ts) - Convert an object with `readonly` properties into a mutable object. Inverse of `Readonly<T>`.
68
+ - [`Mutable`](source/mutable.d.ts) - Convert an object with `readonly` keys into a mutable object. The inverse of `Readonly<T>`.
69
69
  - [`Merge`](source/merge.d.ts) - Merge two types into a new type. Keys of the second type overrides keys of the first type.
70
- - [`MergeExclusive`](source/merge-exclusive.d.ts) - Create a type that has mutually exclusive properties.
71
- - [`RequireAtLeastOne`](source/require-at-least-one.d.ts) - Create a type that requires at least one of the given properties.
72
- - [`ReadonlyDeep`](source/readonly-deep.d.ts) - Create a deeply immutable version of a `object`/`Map`/`Set`/`Array` type.
70
+ - [`MergeExclusive`](source/merge-exclusive.d.ts) - Create a type that has mutually exclusive keys.
71
+ - [`RequireAtLeastOne`](source/require-at-least-one.d.ts) - Create a type that requires at least one of the given keys.
72
+ - [`RequireExactlyOne`](source/require-one.d.ts) - Create a type that requires exactly a single key of the given keys and disallows more.
73
+ - [`PartialDeep`](source/partial-deep.d.ts) - Create a deeply optional version of another type. Use [`Partial<T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1401-L1406) if you only need one level deep.
74
+ - [`ReadonlyDeep`](source/readonly-deep.d.ts) - Create a deeply immutable version of an `object`/`Map`/`Set`/`Array` type. Use [`Readonly<T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1415-L1420) if you only need one level deep.
73
75
  - [`LiteralUnion`](source/literal-union.d.ts) - Create a union type by combining primitive types and literal types without sacrificing auto-completion in IDEs for the literal type part of the union. Workaround for [Microsoft/TypeScript#29729](https://github.com/Microsoft/TypeScript/issues/29729).
74
76
  - [`Promisable`](source/promisable.d.ts) - Create a type that represents either the value or the value wrapped in `PromiseLike`.
77
+ - [`Opaque`](source/opaque.d.ts) - Create an [opaque type](https://codemix.com/opaque-types-in-javascript/).
78
+ - [`SetOptional`](source/set-optional.d.ts) - Create a type that makes the given keys optional.
79
+ - [`SetRequired`](source/set-required.d.ts) - Create a type that makes the given keys required.
75
80
 
76
81
  ### Miscellaneous
77
82
 
@@ -83,6 +88,7 @@ Click the type names for complete docs.
83
88
  *If we decline a type addition, we will make sure to document the better solution here.*
84
89
 
85
90
  - [`Diff` and `Spread`](https://github.com/sindresorhus/type-fest/pull/7) - The PR author didn't provide any real-world use-cases and the PR went stale. If you think this type is useful, provide some real-world use-cases and we might reconsider.
91
+ - [`Dictionary`](https://github.com/sindresorhus/type-fest/issues/33) - You only save a few characters (`Dictionary<number>` vs `Record<string, number>`) from [`Record`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1429-L1434), which is more flexible and well-known. Also, you shouldn't use an object as a dictionary. We have `Map` in JavaScript now.
86
92
 
87
93
 
88
94
  ## Tips
@@ -92,17 +98,514 @@ Click the type names for complete docs.
92
98
  There are many advanced types most users don't know about.
93
99
 
94
100
  - [`Partial<T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1401-L1406) - Make all properties in `T` optional.
101
+ <details>
102
+ <summary>
103
+ Example
104
+ </summary>
105
+
106
+ [Playground](https://typescript-play.js.org/?target=6#code/KYOwrgtgBAMg9gcxsAbsANlA3gKClAeQDMiAaPKAEWACMwFz8BRAJxbhcagDEBDAF17ocAXxw4AliH7AWRXgGNgUAHJwAJsADCcEEQkJsFXgAcTK3hGAAuKAGd+LKQgDcFEx363wEGrLf46IjIaOi28EioGG5iOArovHZ2qhrAAIJmAEJgEuiaLEb4Jk4oAsoKuvoIYCwCErq2apo6egZQALyF+FCm5pY2UABETelmg1xFnrYAzAAM8xNQQZGh4cFR6AB0xEQUIm4UFa0IABRHVbYACrws-BJCADwjLVUAfACUXfhEHFBnug4oABrYAATygcCIhBoACtgAp+JsQaC7P9ju9Prhut0joCwCZ1GUAGpCMDKTrnAwAbWRPWSyMhKWalQMAF0Dtj8BIoSd8YSZCT0GSOu1OmAQJp9CBgOpPkc7uBgBzOfwABYSOybSnVWp3XQ0sF04FgxnPFkIVkdKB84mkpUUfCxbEsYD8GogKBqjUBKBiWIAen9UGut3u6CeqReBlePXQQQA7skwMl+HAoMU4CgJJoISB0ODeOmbvwIVC1cAcIGmdpzVApDI5IpgJscNL49WMiZsrl8id3lrzScsD0zBYrLZBgAVOCUOCdwa+95uIA)
107
+
108
+ ```ts
109
+ interface NodeConfig {
110
+ appName: string;
111
+ port: number;
112
+ }
113
+
114
+ class NodeAppBuilder {
115
+ private configuration: NodeConfig = {
116
+ appName: 'NodeApp',
117
+ port: 3000
118
+ };
119
+
120
+ config(config: Partial<NodeConfig>) {
121
+ type NodeConfigKey = keyof NodeConfig;
122
+
123
+ for (const key of Object.keys(config) as NodeConfigKey[]) {
124
+ const updateValue = config[key];
125
+
126
+ if (updateValue === undefined) {
127
+ continue;
128
+ }
129
+
130
+ this.configuration[key] = updateValue;
131
+ }
132
+
133
+ return this;
134
+ }
135
+ }
136
+
137
+ // `Partial<NodeConfig>`` allows us to provide only a part of the
138
+ // NodeConfig interface.
139
+ new NodeAppBuilder().config({appName: 'ToDoApp'});
140
+ ```
141
+ </details>
142
+
95
143
  - [`Required<T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1408-L1413) - Make all properties in `T` required.
144
+ <details>
145
+ <summary>
146
+ Example
147
+ </summary>
148
+
149
+ [Playground](https://typescript-play.js.org/?target=6#code/AQ4SwOwFwUwJwGYEMDGNgGED21VQGJZwC2wA3gFCjXAzFJgA2A-AFzADOUckA5gNxUaIYjA4ckvGG07c+g6gF8KQkAgCuEFFDA5O6gEbEwUbLm2ESwABQIixACJIoSdgCUYAR3Vg4MACYAPGYuFvYAfACU5Ko0APRxwADKMBD+wFAAFuh2Vv7OSBlYGdmc8ABu8LHKsRyGxqY4oQT21pTCIHQMjOwA5DAAHgACxAAOjDAAdChYxL0ANLHUouKSMH0AEmAAhJhY6ozpAJ77GTCMjMCiV0ToSAb7UJPPC9WRgrEJwAAqR6MwSRQPFGUFocDgRHYxnEfGAowh-zgUCOwF6KwkUl6tXqJhCeEsxDaS1AXSYfUGI3GUxmc0WSneQA)
150
+
151
+ ```ts
152
+ interface ContactForm {
153
+ email?: string;
154
+ message?: string;
155
+ }
156
+
157
+ function submitContactForm(formData: Required<ContactForm>) {
158
+ // Send the form data to the server.
159
+ }
160
+
161
+ submitContactForm({
162
+ email: 'ex@mple.com',
163
+ message: 'Hi! Could you tell me more about…',
164
+ });
165
+
166
+ // TypeScript error: missing property 'message'
167
+ submitContactForm({
168
+ email: 'ex@mple.com',
169
+ });
170
+ ```
171
+ </details>
172
+
96
173
  - [`Readonly<T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1415-L1420) - Make all properties in `T` readonly.
174
+ <details>
175
+ <summary>
176
+ Example
177
+ </summary>
178
+
179
+ [Playground](https://typescript-play.js.org/?target=6#code/AQ4UwOwVwW2AZA9gc3mAbmANsA3gKFCOAHkAzMgGkOJABEwAjKZa2kAUQCcvEu32AMQCGAF2FYBIAL4BufDRABLCKLBcywgMZgEKZOoDCiCGSXI8i4hGEwwALmABnUVxXJ57YFgzZHSVF8sT1BpBSItLGEnJz1kAy5LLy0TM2RHACUwYQATEywATwAeAITjU3MAPnkrCJMXLigtUT4AClxgGztKbyDgaX99I1TzAEokr1BRAAslJwA6FIqLAF48TtswHp9MHDla9hJGACswZvmyLjAwAC8wVpm5xZHkUZDaMKIwqyWXYCW0oN4sNlsA1h0ug5gAByACyBQAggAHJHQ7ZBIFoXbzBjMCz7OoQP5YIaJNYQMAAdziCVaALGNSIAHomcAACoFJFgADKWjcSNEwG4vC4ji0wggEEQguiTnMEGALWAV1yAFp8gVgEjeFyuKICvMrCTgVxnst5jtsGC4ljsPNhXxGaAWcAAOq6YRXYDCRg+RWIcA5JSC+kWdCepQ+v3RYCU3RInzRMCGwlpC19NYBW1Ye08R1AA)
180
+
181
+ ```ts
182
+ enum LogLevel {
183
+ Off,
184
+ Debug,
185
+ Error,
186
+ Fatal
187
+ };
188
+
189
+ interface LoggerConfig {
190
+ name: string;
191
+ level: LogLevel;
192
+ }
193
+
194
+ class Logger {
195
+ config: Readonly<LoggerConfig>;
196
+
197
+ constructor({name, level}: LoggerConfig) {
198
+ this.config = {name, level};
199
+ Object.freeze(this.config);
200
+ }
201
+ }
202
+
203
+ const config: LoggerConfig = {
204
+ name: 'MyApp',
205
+ level: LogLevel.Debug
206
+ };
207
+
208
+ const logger = new Logger(config);
209
+
210
+ // TypeScript Error: cannot assign to read-only property.
211
+ logger.config.level = LogLevel.Error;
212
+
213
+ // We are able to edit config variable as we please.
214
+ config.level = LogLevel.Error;
215
+ ```
216
+ </details>
217
+
97
218
  - [`Pick<T, K>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1422-L1427) - From `T`, pick a set of properties whose keys are in the union `K`.
219
+ <details>
220
+ <summary>
221
+ Example
222
+ </summary>
223
+
224
+ [Playground](https://typescript-play.js.org/?target=6#code/AQ4SwOwFwUwJwGYEMDGNgEE5TCgNugN4BQoZwOUBAXMAM5RyQDmA3KeSFABYCuAtgCMISMHloMmENh04oA9tBjQJjFuzIBfYrOAB6PcADCcGElh1gEGAHcKATwAO6ebyjB5CTNlwFwSxFR0BX5HeToYABNgBDh5fm8cfBg6AHIKG3ldA2BHOOcfFNpUygJ0pAhokr4hETFUgDpswywkggAFUwA3MFtgAF5gQgowKhhVKTYKGuFRcXo1aVZgbTIoJ3RW3xhOmB6+wfbcAGsAHi3kgBpgEtGy4AAfG54BWfqAPnZm4AAlZUj4MAkMA8GAGB4vEgfMlLLw6CwPBA8PYRmMgZVgAC6CgmI4cIommQELwICh8RBgKZKvALh1ur0bHQABR5PYMui0Wk7em2ADaAF0AJS0AASABUALIAGQAogR+Mp3CROCAFBBwVC2ikBpj5CgBIqGjizLA5TAFdAmalImAuqlBRoVQh5HBgEy1eDWfs7J5cjzGYKhroVfpDEhHM4MV6GRR5NN0JrtnRg6BVirTFBeHAKYmYY6QNpdB73LmCJZBlSAXAubtvczeSmQMNSuMbmKNgBlHFgPEUNwusBIPAAQlS1xetTmxT0SDoESgdD0C4aACtHMwxytLrohawgA)
225
+
226
+ ```ts
227
+ interface Article {
228
+ title: string;
229
+ thumbnail: string;
230
+ content: string;
231
+ }
232
+
233
+ // Creates new type out of the `Article` interface composed
234
+ // from the Articles' two properties: `title` and `thumbnail`.
235
+ // `ArticlePreview = {title: string; thumbnail: string}`
236
+ type ArticlePreview = Pick<Article, 'title' | 'thumbnail'>;
237
+
238
+ // Render a list of articles using only title and description.
239
+ function renderArticlePreviews(previews: ArticlePreview[]): HTMLElement {
240
+ const articles = document.createElement('div');
241
+
242
+ for (const preview of previews) {
243
+ // Append preview to the articles.
244
+ }
245
+
246
+ return articles;
247
+ }
248
+
249
+ const articles = renderArticlePreviews([
250
+ {
251
+ title: 'TypeScript tutorial!',
252
+ thumbnail: '/assets/ts.jpg'
253
+ }
254
+ ]);
255
+ ```
256
+ </details>
257
+
98
258
  - [`Record<K, T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1429-L1434) - Construct a type with a set of properties `K` of type `T`.
259
+ <details>
260
+ <summary>
261
+ Example
262
+ </summary>
263
+
264
+ [Playground](https://typescript-play.js.org/?target=6#code/AQ4ejYAUHsGcCWAXBMB2dgwGbAKYC2ADgDYwCeeemCaWArgE7ADGMxAhmuQHQBQoYEnJE8wALKEARnkaxEKdMAC8wAOS0kstGuAAfdQBM8ANzxlRjXQbVaWACwC0JPB0NqA3HwGgIwAJJoWozYHCxixnAsjAhStADmwESMMJYo1Fi4HMCIaPEu+MRklHj8gpqyoeHAAKJFFFTAAN4+giDYCIxwSAByHAR4AFw5SDF5Xm2gJBzdfQPD3WPxE5PAlBxdAPLYNQAelgh4aOHDaPQEMowrIAC+3oJ+AMKMrlrAXFhSAFZ4LEhC9g4-0BmA4JBISXgiCkBQABpILrJ5MhUGhYcATGD6Bk4Hh-jNgABrPDkOBlXyQAAq9ngYmJpOAAHcEOCRjAXqwYODfoo6DhakUSph+Uh7GI4P0xER4Cj0OSQGwMP8tP1hgAlX7swwAHgRl2RvIANALSA08ABtAC6AD4VM1Wm0Kow0MMrYaHYJjGYLLJXZb3at1HYnC43Go-QHQDcvA6-JsmEJXARgCDgMYWAhjIYhDAU+YiMAAFIwex0ZmilMITCGF79TLAGRsAgJYAAZRwSEZGzEABFTOZUrJ5Yn+jwnWgeER6HB7AAKJrADpdXqS4ZqYultTG6azVfqHswPBbtauLY7fayQ7HIbAAAMwBuAEoYw9IBq2Ixs9h2eFMOQYPQObALQKJgggABeYhghCIpikkKRpOQRIknAsZUiIeCttECBEP8NSMCkjDDAARMGziuIYxHwYOjDCMBmDNnAuTxA6irdCOBB1Lh5Dqpqn66tISIykawBnOCtqqC0gbjqc9DgpGkxegOliyfJDrRkAA)
265
+
266
+ ```ts
267
+ // Positions of employees in our company.
268
+ type MemberPosition = 'intern' | 'developer' | 'tech-lead';
269
+
270
+ // Interface describing properties of a single employee.
271
+ interface Employee {
272
+ firstName: string;
273
+ lastName: string;
274
+ yearsOfExperience: number;
275
+ }
276
+
277
+ // Create an object that has all possible `MemberPosition` values set as keys.
278
+ // Those keys will store a collection of Employees of the same position.
279
+ const team: Record<MemberPosition, Employee[]> = {
280
+ intern: [],
281
+ developer: [],
282
+ 'tech-lead': [],
283
+ };
284
+
285
+ // Our team has decided to help John with his dream of becoming Software Developer.
286
+ team.intern.push({
287
+ firstName: 'John',
288
+ lastName: 'Doe',
289
+ yearsOfExperience: 0
290
+ });
291
+
292
+ // `Record` forces you to initialize all of the property keys.
293
+ // TypeScript Error: "tech-lead" property is missing
294
+ const teamEmpty: Record<MemberPosition, null> = {
295
+ intern: null,
296
+ developer: null,
297
+ };
298
+ ```
299
+ </details>
300
+
99
301
  - [`Exclude<T, U>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1436-L1439) - Exclude from `T` those types that are assignable to `U`.
302
+ <details>
303
+ <summary>
304
+ Example
305
+ </summary>
306
+
307
+ [Playground](https://typescript-play.js.org/?target=6#code/JYOwLgpgTgZghgYwgAgMrQG7QMIHsQzADmyA3gFDLIAOuUYAXMiAK4A2byAPsgM5hRQJHqwC2AI2gBucgF9y5MAE9qKAEoQAjiwj8AEnBAATNtGQBeZAAooWphu26wAGmS3e93bRC8IASgsAPmRDJRlyAHoI5ABRAA8ENhYjFFYOZGVVZBgoXFFkAAM0zh5+QRBhZhYJaAKAOkjogEkQZAQ4X2QAdwALCFbaemRgXmQtFjhOMFwq9K6ULuB0lk6U+HYwZAxJnQaYFhAEMGB8ZCIIMAAFOjAANR2IK0HGWISklIAedCgsKDwCYgAbQA5M9gQBdVzFQJ+JhiSRQMiUYYwayZCC4VHPCzmSzAspCYEBWxgFhQAZwKC+FpgJ43VwARgADH4ZFQSWSBjcZPJyPtDsdTvxKWBvr8rD1DCZoJ5HPopaYoK4EPhCEQmGKcKriLCtrhgEYkVQVT5Nr4fmZLLZtMBbFZgT0wGBqES6ghbHBIJqoBKFdBWQpjfh+DQbhY2tqiHVsbjLMVkAB+ZAAZiZaeQTHOVxu9ySjxNaujNwDVHNvzqbBGkBAdPoAfkQA)
308
+
309
+ ```ts
310
+ interface ServerConfig {
311
+ port: null | string | number;
312
+ }
313
+
314
+ type RequestHandler = (request: Request, response: Response) => void;
315
+
316
+ // Exclude `null` type from `null | string | number`.
317
+ // In case the port is equal to `null`, we will use default value.
318
+ function getPortValue(port: Exclude<ServerConfig['port'], null>): number {
319
+ if (typeof port === 'string') {
320
+ return parseInt(port, 10);
321
+ }
322
+
323
+ return port;
324
+ }
325
+
326
+ function startServer(handler: RequestHandler, config: ServerConfig): void {
327
+ const server = require('http').createServer(handler);
328
+
329
+ const port = config.port === null ? 3000 : getPortValue(config.port);
330
+ server.listen(port);
331
+ }
332
+ ```
333
+ </details>
334
+
100
335
  - [`Extract<T, U>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1441-L1444) - Extract from `T` those types that are assignable to `U`.
336
+ <details>
337
+ <summary>
338
+ Example
339
+ </summary>
340
+
341
+ [Playground](https://typescript-play.js.org/?target=6#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXzSwEdkQBJYACgEoAueVZAWwCMQYBuAKDDwGcM8MgBF4AXngBlAJ6scESgHIRi6ty5ZUGdoihgEABXZ888AN5d48ANoiAuvUat23K6ihMQ9ATE0BzV3goPy8GZjZOLgBfLi4Aejj4AEEICBwAdz54MAALKFQQ+BxEeAAHY1NgKAwoIKy0grr4DByEUpgccpgMaXgAaxBerCzi+B9-ZulygDouFHRsU1z8kKMYE1RhaqgAHkt4AHkWACt4EAAPbVRgLLWNgBp9gGlBs8uQa6yAUUuYPQwdgNpKM7nh7mMML4CgA+R5WABqUAgpDeVxuhxO1he0jsXGh8EoOBO9COx3BQPo2PBADckaR6IjkSA6PBqTgsMBzPsicdrEC7OJWXSQNwYvFEgAVTS9JLXODpeDpKBZFg4GCoWa8VACIJykAKiQWKy2YQOAioYikCg0OEMDyhRSy4DyxS24KhAAMjyi6gS8AAwjh5OD0iBFHAkJoEOksC1mnkMJq8gUQKDNttKPlnfrwYp3J5XfBHXqoKpfYkAOI4ansTxaeDADmoRSCCBYAbxhC6TDx6rwYHIRX5bScjA4bLJwoDmDwDkfbA9JMrVMVdM1TN69LgkTgwgkchUahqIA)
342
+
343
+ ```ts
344
+ declare function uniqueId(): number;
345
+
346
+ const ID = Symbol('ID');
347
+
348
+ interface Person {
349
+ [ID]: number;
350
+ name: string;
351
+ age: number;
352
+ }
353
+
354
+ // Allows changing the person data as long as the property key is of string type.
355
+ function changePersonData<
356
+ Obj extends Person,
357
+ Key extends Extract<keyof Person, string>,
358
+ Value extends Obj[Key]
359
+ > (obj: Obj, key: Key, value: Value): void {
360
+ obj[key] = value;
361
+ }
362
+
363
+ // Tiny Andrew was born.
364
+ const andrew = {
365
+ [ID]: uniqueId(),
366
+ name: 'Andrew',
367
+ age: 0,
368
+ };
369
+
370
+ // Cool, we're fine with that.
371
+ changePersonData(andrew, 'name', 'Pony');
372
+
373
+ // Goverment didn't like the fact that you wanted to change your identity.
374
+ changePersonData(andrew, ID, uniqueId());
375
+ ```
376
+ </details>
377
+
101
378
  - [`NonNullable<T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1446-L1449) - Exclude `null` and `undefined` from `T`.
379
+ <details>
380
+ <summary>
381
+ Example
382
+ </summary>
383
+ Works with <code>strictNullChecks</code> set to <code>true</code>. (Read more <a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html">here</a>)
384
+
385
+ [Playground](https://typescript-play.js.org/?target=6#code/C4TwDgpgBACg9gJ2AOQK4FsBGEFQLxQDOwCAlgHYDmUAPlORtrnQwDasDcAUFwPQBU-WAEMkUOADMowqAGNWwwoSgATCBIqlgpOOSjAAFsOBRSy1IQgr9cKJlSlW1mZYQA3HFH68u8xcoBlHA8EACEHJ08Aby4oKDBUTFZSWXjEFEYcAEIALihkXTR2YSSIAB54JDQsHAA+blj4xOTUsHSACkMzPKD3HHDHNQQAGjSkPMqMmoQASh7g-oihqBi4uNIpdraxPAI2VhmVxrX9AzMAOm2ppnwoAA4ABifuE4BfKAhWSyOTuK7CS7pao3AhXF5rV48E4ICDAVAIPT-cGQyG+XTEIgLMJLTx7CAAdygvRCA0iCHaMwarhJOIQjUBSHaACJHk8mYdeLwxtdcVAAOSsh58+lXdr7Dlcq7A3n3J4PEUdADMcspUE53OluAIUGVTx46oAKuAIAFZGQwCYAKIIBCILjUxaDHAMnla+iodjcIA)
386
+
387
+ ```ts
388
+ type PortNumber = string | number | null;
389
+
390
+ /** Part of a class definition that is used to build a server */
391
+ class ServerBuilder {
392
+ portNumber!: NonNullable<PortNumber>;
393
+
394
+ port(this: ServerBuilder, port: PortNumber): ServerBuilder {
395
+ if (port == null) {
396
+ this.portNumber = 8000;
397
+ } else {
398
+ this.portNumber = port;
399
+ }
400
+
401
+ return this;
402
+ }
403
+ }
404
+
405
+ const serverBuilder = new ServerBuilder();
406
+
407
+ serverBuilder
408
+ .port('8000') // portNumber = '8000'
409
+ .port(null) // portNumber = 8000
410
+ .port(3000); // portNumber = 3000
411
+
412
+ // TypeScript error
413
+ serverBuilder.portNumber = null;
414
+ ```
415
+ </details>
416
+
102
417
  - [`Parameters<T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1451-L1454) - Obtain the parameters of a function type in a tuple.
418
+ <details>
419
+ <summary>
420
+ Example
421
+ </summary>
422
+
423
+ [Playground](https://typescript-play.js.org/?target=6#code/GYVwdgxgLglg9mABAZwBYmMANgUwBQxgAOIUAXIgIZgCeA2gLoCUFAbnDACaIDeAUIkQB6IYgCypSlBxUATrMo1ECsJzgBbLEoipqAc0J7EMKMgDkiHLnU4wp46pwAPHMgB0fAL58+oSLARECEosLAA5ABUYG2QAHgAxJGdpVWREPDdMylk9ZApqemZEAF4APipacrw-CApEgBogkKwAYThwckQwEHUAIxxZJl4BYVEImiIZKF0oZRwiWVdbeygJmThgOYgcGFYcbhqApCJsyhtpWXcR1cnEePBoeDAABVPzgbTixFeFd8uEsClADcIxGiygIFkSEOT3SmTc2VydQeRx+ZxwF2QQ34gkEwDgsnSuFmMBKiAADEDjIhYk1Qm0OlSYABqZnYka4xA1DJZHJYkGc7yCbyeRA+CAIZCzNAYbA4CIAdxg2zJwVCkWirjwMswuEaACYmCCgA)
424
+
425
+ ```ts
426
+ function shuffle(input: any[]): void {
427
+ // Mutate array randomly changing its' elements indexes.
428
+ }
429
+
430
+ function callNTimes<Fn extends (...args: any[]) => any> (func: Fn, callCount: number) {
431
+ // Type that represents the type of the received function parameters.
432
+ type FunctionParameters = Parameters<Fn>;
433
+
434
+ return function (...args: FunctionParameters) {
435
+ for (let i = 0; i < callCount; i++) {
436
+ func(...args);
437
+ }
438
+ }
439
+ }
440
+
441
+ const shuffleTwice = callNTimes(shuffle, 2);
442
+ ```
443
+ </details>
444
+
103
445
  - [`ConstructorParameters<T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1456-L1459) - Obtain the parameters of a constructor function type in a tuple.
446
+ <details>
447
+ <summary>
448
+ Example
449
+ </summary>
450
+
451
+ [Playground](https://typescript-play.js.org/?target=6#code/MYGwhgzhAECCBOAXAlqApgWQPYBM0mgG8AoaaFRENALmgkXmQDsBzAblOmCycTV4D8teo1YdO3JiICuwRFngAKClWENmLAJRFOZRAAtkEAHQq00ALzlklNBzIBfYk+KhIMAJJTEYJsDQAwmDA+mgAPAAq0GgAHnxMODCKTGgA7tCKxllg8CwQtL4AngDaALraFgB80EWa1SRkAA6MAG5gfNAB4FABPDJyCrQR9tDNyG0dwMGhtBhgjWEiGgA00F70vv4RhY3hEZXVVinpc42KmuJkkv3y8Bly8EPaDWTkhiZd7r3e8LK3llwGCMXGQWGhEOsfH5zJlsrl8p0+gw-goAAo5MAAW3BaHgEEilU0tEhmzQ212BJ0ry4SOg+kg+gBBiMximIGA0nAfAQLGk2N4EAAEgzYcYcnkLsRdDTvNEYkYUKwSdCme9WdM0MYwYhFPSIPpJdTkAAzDKxBUaZX+aAAQgsVmkCTQxuYaBw2ng4Ok8CYcotSu8pMur09iG9vuObxZnx6SN+AyUWTF8MN0CcZE4Ywm5jZHK5aB5fP4iCFIqT4oRRTKRLo6lYVNeAHpG50wOzOe1zHr9NLQ+HoABybsD4HOKXXRA1JCoKhBELmI5pNaB6Fz0KKBAodDYPAgSUTmqYsAALx4m5nC6nW9nGq14KtaEUA9gR9PvuNCjQ9BgACNvcwNBtAcLiAA)
452
+
453
+ ```ts
454
+ class ArticleModel {
455
+ title: string;
456
+ content?: string;
457
+
458
+ constructor(title: string) {
459
+ this.title = title;
460
+ }
461
+ }
462
+
463
+ class InstanceCache<T extends (new (...args: any[]) => any)> {
464
+ private ClassConstructor: T;
465
+ private cache: Map<string, InstanceType<T>> = new Map();
466
+
467
+ constructor (ctr: T) {
468
+ this.ClassConstructor = ctr;
469
+ }
470
+
471
+ getInstance (...args: ConstructorParameters<T>): InstanceType<T> {
472
+ const hash = this.calculateArgumentsHash(...args);
473
+
474
+ const existingInstance = this.cache.get(hash);
475
+ if (existingInstance !== undefined) {
476
+ return existingInstance;
477
+ }
478
+
479
+ return new this.ClassConstructor(...args);
480
+ }
481
+
482
+ private calculateArgumentsHash(...args: any[]): string {
483
+ // Calculate hash.
484
+ return 'hash';
485
+ }
486
+ }
487
+
488
+ const articleCache = new InstanceCache(ArticleModel);
489
+ const amazonArticle = articleCache.getInstance('Amazon forests burining!');
490
+ ```
491
+ </details>
492
+
104
493
  - [`ReturnType<T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1461-L1464) – Obtain the return type of a function type.
494
+ <details>
495
+ <summary>
496
+ Example
497
+ </summary>
498
+
499
+ [Playground](https://typescript-play.js.org/?target=6#code/MYGwhgzhAECSAmICmBlJAnAbgS2E6A3gFDTTwD2AcuQC4AW2AdgOYAUAlAFzSbnbyEAvkWFFQkGJSQB3GMVI1sNZNwg10TZgG4S0YOUY0kh1es07d+xmvQBXYDXLpWi5UlMaWAGj0GjJ6BtNdkJdBQYIADpXZGgAXmgYpB1ScOwoq38aeN9DYxoU6GFRKzVoJjUwRjwAYXJbPPRuAFkwAAcAHgAxBodsAx9GWwBbACMMAD4cxhloVraOCyYjdAAzMDxoOut1e0d0UNIZ6WhWSPOwdGYIbiqATwBtAF0uaHudUQB6ACpv6ABpJBINqJdAbADW0Do5BOw3u5R2VTwMHIq2gAANtjZ0bkbHsnFCwJh8ONjHp0EgwEZ4JFoN9PkRVr1FAZoMwkDRYIjqkgOrosepoEgAB7+eAwAV2BxOLy6ACCVxgIrFEoMeOl6AACpcwMMORgIB1JRMiBNWKVdhruJKfOdIpdrtwFddXlzKjyACp3Nq842HaDIbL6BrZBIVGhIpB1EMYSLsmjmtWW-YhAA+qegAAYLKQLQj3ZsEsdccmnGcLor2Dn8xGedHGpEIBzEzspfsfMHDNAANTQACMVaIljV5GQkRA5DYmIpVKQAgAJARO9le33BDXIyi0YuLW2nJFGLqkOvxFB0YPdBSaLZ0IwNzyPkO8-xkGgsLh8Al427a3hWAhXwwHA8EHT5PmgAB1bAQBAANJ24adKWpft72RaBUTgRBUCAj89HAM8xCTaBjggABRQx0DuHJv25P9dCkWRZVIAAiBjoFImpmjlFBgA0NpsjadByDacgIDAEAIAAQmYpjoGYgAZSBsmGPw6DtZiiFA8CoJguDmAQmoZ2QvtUKQLdoAYmBTwgdEiCAA)
500
+
501
+ ```ts
502
+ /** Provides every element of the iterable `iter` into the `callback` function and stores the results in an array. */
503
+ function mapIter<
504
+ Elem,
505
+ Func extends (elem: Elem) => any,
506
+ Ret extends ReturnType<Func>
507
+ >(iter: Iterable<Elem>, callback: Func): Ret[] {
508
+ const mapped: Ret[] = [];
509
+
510
+ for (const elem of iter) {
511
+ mapped.push(callback(elem));
512
+ }
513
+
514
+ return mapped;
515
+ }
516
+
517
+ const setObject: Set<string> = new Set();
518
+ const mapObject: Map<number, string> = new Map();
519
+
520
+ mapIter(setObject, (value: string) => value.indexOf('Foo')); // number[]
521
+
522
+ mapIter(mapObject, ([key, value]: [number, string]) => {
523
+ return key % 2 === 0 ? value : 'Odd';
524
+ }); // string[]
525
+ ```
526
+ </details>
527
+
105
528
  - [`InstanceType<T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1466-L1469) – Obtain the instance type of a constructor function type.
529
+ <details>
530
+ <summary>
531
+ Example
532
+ </summary>
533
+
534
+ [Playground](https://typescript-play.js.org/?target=6#code/MYGwhgzhAECSAmICmBlJAnAbgS2E6A3gFDTTwD2AcuQC4AW2AdgOYAUAlAFzSbnbyEAvkWFFQkGJSQB3GMVI1sNZNwg10TZgG4S0YOUY0kh1es07d+xmvQBXYDXLpWi5UlMaWAGj0GjJ6BtNdkJdBQYIADpXZGgAXmgYpB1ScOwoq38aeN9DYxoU6GFRKzVoJjUwRjwAYXJbPPRuAFkwAAcAHgAxBodsAx9GWwBbACMMAD4cxhloVraOCyYjdAAzMDxoOut1e0d0UNIZ6WhWSPOwdGYIbiqATwBtAF0uaHudUQB6ACpv6ABpJBINqJdAbADW0Do5BOw3u5R2VTwMHIq2gAANtjZ0bkbHsnFCwJh8ONjHp0EgwEZ4JFoN9PkRVr1FAZoMwkDRYIjqkgOrosepoEgAB7+eAwAV2BxOLy6ACCVxgIrFEoMeOl6AACpcwMMORgIB1JRMiBNWKVdhruJKfOdIpdrtwFddXlzKjyACp3Nq842HaDIbL6BrZBIVGhIpB1EMYSLsmjmtWW-YhAA+qegAAYLKQLQj3ZsEsdccmnGcLor2Dn8xGedHGpEIBzEzspfsfMHDNAANTQACMVaIljV5GQkRA5DYmIpVKQAgAJARO9le33BDXIyi0YuLW2nJFGLqkOvxFB0YPdBSaLZ0IwNzyPkO8-xkGgsLh8Al427a3hWAhXwwHA8EHT5PmgAB1bAQBAANJ24adKWpft72RaBUTgRBUCAj89HAM8xCTaBjggABRQx0DuHJv25P9dCkWRZVIAAiBjoFImpmjlFBgA0NpsjadByDacgIDAEAIAAQmYpjoGYgAZSBsmGPw6DtZiiFA8CoJguDmAQmoZ2QvtUKQLdoAYmBTwgdEiCAA)
535
+
536
+ ```ts
537
+ class IdleService {
538
+ doNothing (): void {}
539
+ }
540
+
541
+ class News {
542
+ title: string;
543
+ content: string;
544
+
545
+ constructor(title: string, content: string) {
546
+ this.title = title;
547
+ this.content = content;
548
+ }
549
+ }
550
+
551
+ const instanceCounter: Map<Function, number> = new Map();
552
+
553
+ interface Constructor {
554
+ new(...args: any[]): any;
555
+ }
556
+
557
+ // Keep track how many instances of `Constr` constructor have been created.
558
+ function getInstance<
559
+ Constr extends Constructor,
560
+ Args extends ConstructorParameters<Constr>
561
+ >(constructor: Constr, ...args: Args): InstanceType<Constr> {
562
+ let count = instanceCounter.get(constructor) || 0;
563
+
564
+ const instance = new constructor(...args);
565
+
566
+ instanceCounter.set(constructor, count + 1);
567
+
568
+ console.log(`Created ${count + 1} instances of ${Constr.name} class`);
569
+
570
+ return instance;
571
+ }
572
+
573
+
574
+ const idleService = getInstance(IdleService);
575
+ // Will log: `Created 1 instances of IdleService class`
576
+ const newsEntry = getInstance(News, 'New ECMAScript proposals!', 'Last month...');
577
+ // Will log: `Created 1 instances of News class`
578
+ ```
579
+ </details>
580
+
581
+ - [`Omit<T, K>`](https://github.com/microsoft/TypeScript/blob/71af02f7459dc812e85ac31365bfe23daf14b4e4/src/lib/es5.d.ts#L1446) – Constructs a type by picking all properties from T and then removing K.
582
+ <details>
583
+ <summary>
584
+ Example
585
+ </summary>
586
+
587
+ [Playground](https://typescript-play.js.org/?target=6#code/JYOwLgpgTgZghgYwgAgIImAWzgG2QbwChlks4BzCAVShwC5kBnMKUcgbmKYAcIFgIjBs1YgOXMpSFMWbANoBdTiW5woFddwAW0kfKWEAvoUIB6U8gDCUCHEiNkICAHdkYAJ69kz4GC3JcPG4oAHteKDABBxCYNAxsPFBIWEQUCAAPJG4wZABySUFcgJAAEzMLXNV1ck0dIuCw6EjBADpy5AB1FAQ4EGQAV0YUP2AHDy8wEOQbUugmBLwtEIA3OcmQnEjuZBgQqE7gAGtgZAhwKHdkHFGwNvGUdDIcAGUliIBJEF3kAF5kAHlML4ADyPBIAGjyBUYRQAPnkqho4NoYQA+TiEGD9EAISIhPozErQMG4AASK2gn2+AApek9pCSXm8wFSQooAJQMUkAFQAsgAZACiOAgmDOOSIJAQ+OYyGl4DgoDmf2QJRCCH6YvALQQNjsEGFovF1NyJWAy1y7OUyHMyE+yRAuFImG4Iq1YDswHxbRINjA-SgfXlHqVUE4xiAA)
588
+
589
+ ```ts
590
+ interface Animal {
591
+ imageUrl: string;
592
+ species: string;
593
+ images: string[];
594
+ paragraphs: string[];
595
+ }
596
+
597
+ // Creates new type with all properties of the `Animal` interface
598
+ // except 'images' and 'paragraphs' properties. We can use this
599
+ // type to render small hover tooltip for a wiki entry list.
600
+ type AnimalShortInfo = Omit<Animal, 'images' | 'paragraphs'>;
601
+
602
+ function renderAnimalHoverInfo (animals: AnimalShortInfo[]): HTMLElement {
603
+ const container = document.createElement('div');
604
+ // Internal implementation.
605
+ return container;
606
+ }
607
+ ```
608
+ </details>
106
609
 
107
610
  You can find some examples in the [TypeScript docs](https://www.typescriptlang.org/docs/handbook/advanced-types.html#predefined-conditional-types).
108
611
 
@@ -117,3 +620,16 @@ You can find some examples in the [TypeScript docs](https://www.typescriptlang.o
117
620
  ## License
118
621
 
119
622
  (MIT OR CC0-1.0)
623
+
624
+
625
+ ---
626
+
627
+ <div align="center">
628
+ <b>
629
+ <a href="https://tidelift.com/subscription/pkg/npm-type-fest?utm_source=npm-type-fest&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a>
630
+ </b>
631
+ <br>
632
+ <sub>
633
+ Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies.
634
+ </sub>
635
+ </div>
package/source/basic.d.ts CHANGED
@@ -17,7 +17,7 @@ export type Primitive =
17
17
  /**
18
18
  Matches a [`class` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes).
19
19
  */
20
- export type Class<T = unknown> = new(...arguments_: any[]) => T;
20
+ export type Class<T = unknown, Arguments extends any[] = any[]> = new(...arguments_: Arguments) => T;
21
21
 
22
22
  /**
23
23
  Matches any [typed array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray), like `Uint8Array` or `Float64Array`.
@@ -2,7 +2,7 @@
2
2
  type Without<FirstType, SecondType> = {[KeyType in Exclude<keyof FirstType, keyof SecondType>]?: never};
3
3
 
4
4
  /**
5
- Create a type that has mutually exclusive properties.
5
+ Create a type that has mutually exclusive keys.
6
6
 
7
7
  This type was inspired by [this comment](https://github.com/Microsoft/TypeScript/issues/14094#issuecomment-373782604).
8
8
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- Convert an object with `readonly` properties into a mutable object. Inverse of `Readonly<T>`.
2
+ Convert an object with `readonly` keys into a mutable object. Inverse of `Readonly<T>`.
3
3
 
4
4
  This can be used to [store and mutate options within a class](https://github.com/sindresorhus/pageres/blob/4a5d05fca19a5fbd2f53842cbf3eb7b1b63bddd2/source/index.ts#L72), [edit `readonly` objects within tests](https://stackoverflow.com/questions/50703834), and [construct a `readonly` object within a function](https://github.com/Microsoft/TypeScript/issues/24509).
5
5
 
@@ -17,6 +17,6 @@ mutableFoo.a = 3;
17
17
  ```
18
18
  */
19
19
  export type Mutable<ObjectType> = {
20
- // For each `Key` in the keys of `ObjectType`, make a mapped type by removing the `readonly` modifier from the property.
20
+ // For each `Key` in the keys of `ObjectType`, make a mapped type by removing the `readonly` modifier from the key.
21
21
  -readonly [KeyType in keyof ObjectType]: ObjectType[KeyType];
22
22
  };
@@ -0,0 +1,40 @@
1
+ /**
2
+ Create an opaque type, which hides its internal details from the public, and can only be created by being used explicitly.
3
+
4
+ The generic type parameter can be anything. It doesn't have to be an object.
5
+
6
+ [Read more about opaque types.](https://codemix.com/opaque-types-in-javascript/)
7
+
8
+ There have been several discussions about adding this feature to TypeScript via the `opaque type` operator, similar to how Flow does it. Unfortunately, nothing has (yet) moved forward:
9
+ - [Microsoft/TypeScript#15408](https://github.com/Microsoft/TypeScript/issues/15408)
10
+ - [Microsoft/TypeScript#15807](https://github.com/Microsoft/TypeScript/issues/15807)
11
+
12
+ @example
13
+ ```
14
+ import {Opaque} from 'type-fest';
15
+
16
+ type AccountNumber = Opaque<number>;
17
+ type AccountBalance = Opaque<number>;
18
+
19
+ function createAccountNumber(): AccountNumber {
20
+ return 2 as AccountNumber;
21
+ }
22
+
23
+ function getMoneyForAccount(accountNumber: AccountNumber): AccountBalance {
24
+ return 4 as AccountBalance;
25
+ }
26
+
27
+ // This will compile successfully.
28
+ getMoneyForAccount(createAccountNumber());
29
+
30
+ // But this won't, because it has to be explicitly passed as an `AccountNumber` type.
31
+ getMoneyForAccount(2);
32
+
33
+ // You can use opaque values like they aren't opaque too.
34
+ const accountNumber = createAccountNumber();
35
+
36
+ // This will compile successfully.
37
+ accountNumber + 2;
38
+ ```
39
+ */
40
+ export type Opaque<Type> = Type & {readonly __opaque__: unique symbol};
@@ -0,0 +1,72 @@
1
+ import {Primitive} from './basic';
2
+
3
+ /**
4
+ Create a type from another type with all keys and nested keys set to optional.
5
+
6
+ Use-cases:
7
+ - Merging a default settings/config object with another object, the second object would be a deep partial of the default object.
8
+ - Mocking and testing complex entities, where populating an entire object with its keys would be redundant in terms of the mock or test.
9
+
10
+ @example
11
+ ```
12
+ import {PartialDeep} from 'type-fest';
13
+
14
+ const settings: Settings = {
15
+ textEditor: {
16
+ fontSize: 14;
17
+ fontColor: '#000000';
18
+ fontWeight: 400;
19
+ }
20
+ autocomplete: false;
21
+ autosave: true;
22
+ };
23
+
24
+ const applySavedSettings = (savedSettings: PartialDeep<Settings>) => {
25
+ return {...settings, ...savedSettings};
26
+ }
27
+
28
+ settings = applySavedSettings({textEditor: {fontWeight: 500}});
29
+ ```
30
+ */
31
+ export type PartialDeep<T> = T extends Primitive
32
+ ? Partial<T>
33
+ : T extends Map<infer KeyType, infer ValueType>
34
+ ? PartialMapDeep<KeyType, ValueType>
35
+ : T extends Set<infer ItemType>
36
+ ? PartialSetDeep<ItemType>
37
+ : T extends ReadonlyMap<infer KeyType, infer ValueType>
38
+ ? PartialReadonlyMapDeep<KeyType, ValueType>
39
+ : T extends ReadonlySet<infer ItemType>
40
+ ? PartialReadonlySetDeep<ItemType>
41
+ : T extends ((...arguments: any[]) => unknown)
42
+ ? T | undefined
43
+ : T extends object
44
+ ? PartialObjectDeep<T>
45
+ : unknown;
46
+
47
+ /**
48
+ Same as `PartialDeep`, but accepts only `Map`s and as inputs. Internal helper for `PartialDeep`.
49
+ */
50
+ interface PartialMapDeep<KeyType, ValueType> extends Map<PartialDeep<KeyType>, PartialDeep<ValueType>> {}
51
+
52
+ /**
53
+ Same as `PartialDeep`, but accepts only `Set`s as inputs. Internal helper for `PartialDeep`.
54
+ */
55
+ interface PartialSetDeep<T> extends Set<PartialDeep<T>> {}
56
+
57
+ /**
58
+ Same as `PartialDeep`, but accepts only `ReadonlyMap`s as inputs. Internal helper for `PartialDeep`.
59
+ */
60
+ interface PartialReadonlyMapDeep<KeyType, ValueType> extends ReadonlyMap<PartialDeep<KeyType>, PartialDeep<ValueType>> {}
61
+
62
+ /**
63
+ Same as `PartialDeep`, but accepts only `ReadonlySet`s as inputs. Internal helper for `PartialDeep`.
64
+ */
65
+ interface PartialReadonlySetDeep<T> extends ReadonlySet<PartialDeep<T>> {}
66
+
67
+ /**
68
+ Same as `PartialDeep`, but accepts only `object`s as inputs. Internal helper for `PartialDeep`.
69
+ */
70
+ type PartialObjectDeep<ObjectType extends object> = {
71
+ [KeyType in keyof ObjectType]?: PartialDeep<ObjectType[KeyType]>
72
+ };
@@ -1,7 +1,7 @@
1
1
  import {Primitive} from './basic';
2
2
 
3
3
  /**
4
- Convert `object`s, `Map`s, `Set`s, and `Array`s and all of their properties/elements into immutable structures recursively.
4
+ Convert `object`s, `Map`s, `Set`s, and `Array`s and all of their keys/elements into immutable structures recursively.
5
5
 
6
6
  This is useful when a deeply nested structure needs to be exposed as completely immutable, for example, an imported JSON module or when receiving an API response that is passed around.
7
7
 
@@ -55,5 +55,5 @@ interface ReadonlySetDeep<ItemType>
55
55
  Same as `ReadonlyDeep`, but accepts only `object`s as inputs. Internal helper for `ReadonlyDeep`.
56
56
  */
57
57
  type ReadonlyObjectDeep<ObjectType extends object> = {
58
- readonly [PropertyType in keyof ObjectType]: ReadonlyDeep<ObjectType[PropertyType]>
58
+ readonly [KeyType in keyof ObjectType]: ReadonlyDeep<ObjectType[KeyType]>
59
59
  };
@@ -1,7 +1,7 @@
1
1
  import {Except} from './except';
2
2
 
3
3
  /**
4
- Create a type that requires at least one of the given properties. The remaining properties are kept as is.
4
+ Create a type that requires at least one of the given keys. The remaining keys are kept as is.
5
5
 
6
6
  @example
7
7
  ```
@@ -28,5 +28,5 @@ export type RequireAtLeastOne<ObjectType, KeysType extends keyof ObjectType = ke
28
28
  Required<Pick<ObjectType, Key>>
29
29
  )
30
30
  }[KeysType]
31
- // …then, make intersection types by adding the remaining properties to each mapped type.
31
+ // …then, make intersection types by adding the remaining keys to each mapped type.
32
32
  & Except<ObjectType, KeysType>;
@@ -0,0 +1,36 @@
1
+ // TODO: Remove this when we target TypeScript >=3.5.
2
+ // eslint-disable-next-line @typescript-eslint/generic-type-naming
3
+ type _Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
4
+
5
+ /**
6
+ Create a type that requires exactly one of the given keys and disallows more. The remaining keys are kept as is.
7
+
8
+ Use-cases:
9
+ - Creating interfaces for components that only need one of the keys to display properly.
10
+ - Declaring generic keys in a single place for a single use-case that gets narrowed down via `RequireExactlyOne`.
11
+
12
+ The caveat with `RequireExactlyOne` is that TypeScript doesn't always know at compile time every key that will exist at runtime. Therefore `RequireExactlyOne` can't do anything to prevent extra keys it doesn't know about.
13
+
14
+ @example
15
+ ```
16
+ import {RequireExactlyOne} from 'type-fest';
17
+
18
+ type Responder = {
19
+ text: () => string;
20
+ json: () => string;
21
+ secure: boolean;
22
+ };
23
+
24
+ const responder: RequireExactlyOne<Responder, 'text' | 'json'> = {
25
+ // Adding a `text` key here would cause a compile error.
26
+
27
+ json: () => '{"message": "ok"}',
28
+ secure: true
29
+ };
30
+ ```
31
+ */
32
+ export type RequireExactlyOne<ObjectType, KeysType extends keyof ObjectType = keyof ObjectType> =
33
+ {[Key in KeysType]: (
34
+ Required<Pick<ObjectType, Key>> &
35
+ Partial<Record<Exclude<KeysType, Key>, never>>
36
+ )}[KeysType] & _Omit<ObjectType, KeysType>;
@@ -0,0 +1,32 @@
1
+ /**
2
+ Create a type that makes the given keys optional. The remaining keys are kept as is. The sister of the `SetRequired` type.
3
+
4
+ Use-case: You want to define a single model where the only thing that changes is whether or not some of the keys are optional.
5
+
6
+ @example
7
+ ```
8
+ import {SetOptional} from 'type-fest';
9
+
10
+ type Foo = {
11
+ a: number;
12
+ b?: string;
13
+ c: boolean;
14
+ }
15
+
16
+ type SomeOptional = SetOptional<Foo, 'b' | 'c'>;
17
+ // type SomeOptional = {
18
+ // a: number;
19
+ // b?: string; // Was already optional and still is.
20
+ // c?: boolean; // Is now optional.
21
+ // }
22
+ ```
23
+ */
24
+ export type SetOptional<BaseType, Keys extends keyof BaseType = keyof BaseType> =
25
+ // Pick just the keys that are not optional from the base type.
26
+ Pick<BaseType, Exclude<keyof BaseType, Keys>> &
27
+ // Pick the keys that should be optional from the base type and make them optional.
28
+ Partial<Pick<BaseType, Keys>> extends
29
+ // If `InferredType` extends the previous, then for each key, use the inferred type key.
30
+ infer InferredType
31
+ ? {[KeyType in keyof InferredType]: InferredType[KeyType]}
32
+ : never;
@@ -0,0 +1,32 @@
1
+ /**
2
+ Create a type that makes the given keys required. The remaining keys are kept as is. The sister of the `SetOptional` type.
3
+
4
+ Use-case: You want to define a single model where the only thing that changes is whether or not some of the keys are required.
5
+
6
+ @example
7
+ ```
8
+ import {SetRequired} from 'type-fest';
9
+
10
+ type Foo = {
11
+ a?: number;
12
+ b: string;
13
+ c?: boolean;
14
+ }
15
+
16
+ type SomeRequired = SetRequired<Foo, 'b' | 'c'>;
17
+ // type SomeRequired = {
18
+ // a?: number;
19
+ // b: string; // Was already required and still is.
20
+ // c: boolean; // Is now required.
21
+ // }
22
+ ```
23
+ */
24
+ export type SetRequired<BaseType, Keys extends keyof BaseType = keyof BaseType> =
25
+ // Pick just the keys that are not required from the base type.
26
+ Pick<BaseType, Exclude<keyof BaseType, Keys>> &
27
+ // Pick the keys that should be required from the base type and make them required.
28
+ Required<Pick<BaseType, Keys>> extends
29
+ // If `InferredType` extends the previous, then for each key, use the inferred type key.
30
+ infer InferredType
31
+ ? {[KeyType in keyof InferredType]: InferredType[KeyType]}
32
+ : never;