type-fest 4.5.0 → 4.7.0
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 +7 -0
- package/package.json +2 -1
- package/readme.md +9 -1
- package/source/array-indices.d.ts +23 -0
- package/source/array-values.d.ts +22 -0
- package/source/conditional-pick-deep.d.ts +4 -2
- package/source/internal.d.ts +32 -0
- package/source/last-array-element.d.ts +18 -8
- package/source/partial-deep.d.ts +13 -15
- package/source/paths.d.ts +107 -0
- package/source/pick-deep.d.ts +141 -0
- package/source/readonly-deep.d.ts +23 -21
- package/source/set-field-type.d.ts +37 -0
- package/source/set-parameter-type.d.ts +68 -0
- package/source/unknown-array.d.ts +25 -0
- package/source/writable-deep.d.ts +29 -9
- package/source/writable.d.ts +35 -7
package/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export type {KeysOfUnion} from './source/keys-of-union';
|
|
|
9
9
|
export type {EmptyObject, IsEmptyObject} from './source/empty-object';
|
|
10
10
|
export type {NonEmptyObject} from './source/non-empty-object';
|
|
11
11
|
export type {UnknownRecord} from './source/unknown-record';
|
|
12
|
+
export type {UnknownArray} from './source/unknown-array';
|
|
12
13
|
export type {Except} from './source/except';
|
|
13
14
|
export type {TaggedUnion} from './source/tagged-union';
|
|
14
15
|
export type {Writable} from './source/writable';
|
|
@@ -24,6 +25,7 @@ export type {OmitIndexSignature} from './source/omit-index-signature';
|
|
|
24
25
|
export type {PickIndexSignature} from './source/pick-index-signature';
|
|
25
26
|
export type {PartialDeep, PartialDeepOptions} from './source/partial-deep';
|
|
26
27
|
export type {RequiredDeep} from './source/required-deep';
|
|
28
|
+
export type {PickDeep} from './source/pick-deep';
|
|
27
29
|
export type {PartialOnUndefinedDeep, PartialOnUndefinedDeepOptions} from './source/partial-on-undefined-deep';
|
|
28
30
|
export type {UndefinedOnPartialDeep} from './source/undefined-on-partial-deep';
|
|
29
31
|
export type {ReadonlyDeep} from './source/readonly-deep';
|
|
@@ -50,6 +52,7 @@ export type {IterableElement} from './source/iterable-element';
|
|
|
50
52
|
export type {Entry} from './source/entry';
|
|
51
53
|
export type {Entries} from './source/entries';
|
|
52
54
|
export type {SetReturnType} from './source/set-return-type';
|
|
55
|
+
export type {SetParameterType} from './source/set-parameter-type';
|
|
53
56
|
export type {Asyncify} from './source/asyncify';
|
|
54
57
|
export type {Simplify} from './source/simplify';
|
|
55
58
|
export type {Jsonify} from './source/jsonify';
|
|
@@ -98,6 +101,10 @@ export type {IsNever} from './source/is-never';
|
|
|
98
101
|
export type {IfNever} from './source/if-never';
|
|
99
102
|
export type {IsUnknown} from './source/is-unknown';
|
|
100
103
|
export type {IfUnknown} from './source/if-unknown';
|
|
104
|
+
export type {ArrayIndices} from './source/array-indices';
|
|
105
|
+
export type {ArrayValues} from './source/array-values';
|
|
106
|
+
export type {SetFieldType} from './source/set-field-type';
|
|
107
|
+
export type {Paths} from './source/paths';
|
|
101
108
|
|
|
102
109
|
// Template literal types
|
|
103
110
|
export type {CamelCase} from './source/camel-case';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "type-fest",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.0",
|
|
4
4
|
"description": "A collection of essential TypeScript types",
|
|
5
5
|
"license": "(MIT OR CC0-1.0)",
|
|
6
6
|
"repository": "sindresorhus/type-fest",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"url": "https://sindresorhus.com"
|
|
12
12
|
},
|
|
13
13
|
"types": "./index.d.ts",
|
|
14
|
+
"sideEffects": false,
|
|
14
15
|
"engines": {
|
|
15
16
|
"node": ">=16"
|
|
16
17
|
},
|
package/readme.md
CHANGED
|
@@ -113,8 +113,9 @@ Click the type names for complete docs.
|
|
|
113
113
|
- [`IsEmptyObject`](source/empty-object.d.ts) - Returns a `boolean` for whether the type is strictly equal to an empty plain object, the `{}` value.
|
|
114
114
|
- [`NonEmptyObject`](source/non-empty-object.d.ts) - Represents an object with at least 1 non-optional key.
|
|
115
115
|
- [`UnknownRecord`](source/unknown-record.d.ts) - Represents an object with `unknown` value. You probably want this instead of `{}`.
|
|
116
|
+
- [`UnknownArray`](source/unknown-array.d.ts) - Represents an array with `unknown` value.
|
|
116
117
|
- [`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/utility-types.html#omittype-keys).
|
|
117
|
-
- [`Writable`](source/writable.d.ts) - Create a type that strips `readonly` from
|
|
118
|
+
- [`Writable`](source/writable.d.ts) - Create a type that strips `readonly` from the given type. Inverse of `Readonly<T>`.
|
|
118
119
|
- [`WritableDeep`](source/writable-deep.d.ts) - Create a deeply mutable version of an `object`/`ReadonlyMap`/`ReadonlySet`/`ReadonlyArray` type. The inverse of `ReadonlyDeep<T>`. Use `Writable<T>` if you only need one level deep.
|
|
119
120
|
- [`Merge`](source/merge.d.ts) - Merge two types into a new type. Keys of the second type overrides keys of the first type.
|
|
120
121
|
- [`MergeDeep`](source/merge-deep.d.ts) - Merge two objects or two arrays/tuples recursively into a new type.
|
|
@@ -125,6 +126,7 @@ Click the type names for complete docs.
|
|
|
125
126
|
- [`RequireAllOrNone`](source/require-all-or-none.d.ts) - Create a type that requires all of the given keys or none of the given keys.
|
|
126
127
|
- [`RequireOneOrNone`](source/require-one-or-none.d.ts) - Create a type that requires exactly a single key of the given keys and disallows more, or none of the given keys.
|
|
127
128
|
- [`RequiredDeep`](source/required-deep.d.ts) - Create a deeply required version of another type. Use [`Required<T>`](https://www.typescriptlang.org/docs/handbook/utility-types.html#requiredtype) if you only need one level deep.
|
|
129
|
+
- [`PickDeep`](source/pick-deep.d.ts) - Pick properties from a deeply-nested object. Use [`Pick<T>`](https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys) if you only need one level deep.
|
|
128
130
|
- [`OmitIndexSignature`](source/omit-index-signature.d.ts) - Omit any index signatures from the given object type, leaving only explicitly defined properties.
|
|
129
131
|
- [`PickIndexSignature`](source/pick-index-signature.d.ts) - Pick only index signatures from the given object type, leaving out all explicitly defined properties.
|
|
130
132
|
- [`PartialDeep`](source/partial-deep.d.ts) - Create a deeply optional version of another type. Use [`Partial<T>`](https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype) if you only need one level deep.
|
|
@@ -154,6 +156,7 @@ Click the type names for complete docs.
|
|
|
154
156
|
- [`Entry`](source/entry.d.ts) - Create a type that represents the type of an entry of a collection.
|
|
155
157
|
- [`Entries`](source/entries.d.ts) - Create a type that represents the type of the entries of a collection.
|
|
156
158
|
- [`SetReturnType`](source/set-return-type.d.ts) - Create a function type with a return type of your choice and the same parameters as the given function type.
|
|
159
|
+
- [`SetParameterType`](source/set-parameter-type.d.ts) - Create a function that replaces some parameters with the given parameters.
|
|
157
160
|
- [`Simplify`](source/simplify.d.ts) - Useful to flatten the type output to improve type hints shown in editors. And also to transform an interface into a type to aide with assignability.
|
|
158
161
|
- [`Get`](source/get.d.ts) - Get a deeply-nested property from an object using a key path, like [Lodash's `.get()`](https://lodash.com/docs/latest#get) function.
|
|
159
162
|
- [`StringKeyOf`](source/string-key-of.d.ts) - Get keys of the given type as strings.
|
|
@@ -172,6 +175,10 @@ Click the type names for complete docs.
|
|
|
172
175
|
- [`IsEqual`](source/is-equal.d.ts) - Returns a boolean for whether the two given types are equal.
|
|
173
176
|
- [`TaggedUnion`](source/tagged-union.d.ts) - Create a union of types that share a common discriminant property.
|
|
174
177
|
- [`IntRange`](source/int-range.d.ts) - Generate a union of numbers.
|
|
178
|
+
- [`ArrayIndices`](source/array-indices.d.ts) - Provides valid indices for a constant array or tuple.
|
|
179
|
+
- [`ArrayValues`](source/array-values.d.ts) - Provides all values for a constant array or tuple.
|
|
180
|
+
- [`SetFieldType`](source/set-field-type.d.ts) - Create a type that changes the type of the given keys.
|
|
181
|
+
- [`Paths`](source/paths.d.ts) - Generate a union of all possible paths to properties in the given object.
|
|
175
182
|
|
|
176
183
|
### Type Guard
|
|
177
184
|
|
|
@@ -937,6 +944,7 @@ You can find some examples in the [TypeScript docs](https://www.typescriptlang.o
|
|
|
937
944
|
## Maintainers
|
|
938
945
|
|
|
939
946
|
- [Sindre Sorhus](https://github.com/sindresorhus)
|
|
947
|
+
- [Haozheng Li](https://github.com/Emiyaaaaa)
|
|
940
948
|
- [Jarek Radosz](https://github.com/CvX)
|
|
941
949
|
- [Dimitri Benin](https://github.com/BendingBender)
|
|
942
950
|
- [Pelle Wessman](https://github.com/voxpelli)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Provides valid indices for a constant array or tuple.
|
|
3
|
+
|
|
4
|
+
Use-case: This type is useful when working with constant arrays or tuples and you want to enforce type-safety for accessing elements by their indices.
|
|
5
|
+
|
|
6
|
+
@example
|
|
7
|
+
```
|
|
8
|
+
import type {ArrayIndices, ArrayValues} from 'type-fest';
|
|
9
|
+
|
|
10
|
+
const weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] as const;
|
|
11
|
+
|
|
12
|
+
type Weekday = ArrayIndices<typeof weekdays>;
|
|
13
|
+
type WeekdayName = ArrayValues<typeof weekdays>;
|
|
14
|
+
|
|
15
|
+
const getWeekdayName = (day: Weekday): WeekdayName => weekdays[day];
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
@see {@link ArrayValues}
|
|
19
|
+
|
|
20
|
+
@category Array
|
|
21
|
+
*/
|
|
22
|
+
export type ArrayIndices<Element extends readonly unknown[]> =
|
|
23
|
+
Exclude<Partial<Element>['length'], Element['length']>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Provides all values for a constant array or tuple.
|
|
3
|
+
|
|
4
|
+
Use-case: This type is useful when working with constant arrays or tuples and you want to enforce type-safety with their values.
|
|
5
|
+
|
|
6
|
+
@example
|
|
7
|
+
```
|
|
8
|
+
import type {ArrayValues, ArrayIndices} from 'type-fest';
|
|
9
|
+
|
|
10
|
+
const weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] as const;
|
|
11
|
+
|
|
12
|
+
type WeekdayName = ArrayValues<typeof weekdays>;
|
|
13
|
+
type Weekday = ArrayIndices<typeof weekdays>;
|
|
14
|
+
|
|
15
|
+
const getWeekdayName = (day: Weekday): WeekdayName => weekdays[day];
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
@see {@link ArrayIndices}
|
|
19
|
+
|
|
20
|
+
@category Array
|
|
21
|
+
*/
|
|
22
|
+
export type ArrayValues<T extends readonly unknown[]> = T[number];
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type {IsEqual} from './is-equal';
|
|
2
2
|
import type {ConditionalExcept} from './conditional-except';
|
|
3
3
|
import type {ConditionalSimplifyDeep} from './conditional-simplify';
|
|
4
|
+
import type {UnknownRecord} from './unknown-record';
|
|
5
|
+
import type {EmptyObject} from './empty-object';
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
Used to mark properties that should be excluded.
|
|
@@ -95,7 +97,7 @@ export type ConditionalPickDeep<
|
|
|
95
97
|
> = ConditionalSimplifyDeep<ConditionalExcept<{
|
|
96
98
|
[Key in keyof Type]: AssertCondition<Type[Key], Condition, Options> extends true
|
|
97
99
|
? Type[Key]
|
|
98
|
-
: Type[Key] extends
|
|
100
|
+
: Type[Key] extends UnknownRecord
|
|
99
101
|
? ConditionalPickDeep<Type[Key], Condition, Options>
|
|
100
102
|
: typeof conditionalPickDeepSymbol;
|
|
101
|
-
}, (typeof conditionalPickDeepSymbol | undefined) |
|
|
103
|
+
}, (typeof conditionalPickDeepSymbol | undefined) | EmptyObject>, never, UnknownRecord>;
|
package/source/internal.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type {Primitive} from './primitive';
|
|
|
2
2
|
import type {Simplify} from './simplify';
|
|
3
3
|
import type {Trim} from './trim';
|
|
4
4
|
import type {IsAny} from './is-any';
|
|
5
|
+
import type {UnknownRecord} from './unknown-record';
|
|
5
6
|
|
|
6
7
|
// TODO: Remove for v5.
|
|
7
8
|
export type {UnknownRecord} from './unknown-record';
|
|
@@ -24,6 +25,37 @@ export type BuildTuple<L extends number, Fill = unknown, T extends readonly unkn
|
|
|
24
25
|
? T
|
|
25
26
|
: BuildTuple<L, Fill, [...T, Fill]>;
|
|
26
27
|
|
|
28
|
+
/**
|
|
29
|
+
Create an object type with the given key `<Key>` and value `<Value>`.
|
|
30
|
+
|
|
31
|
+
It will copy the prefix and optional status of the same key from the given object `CopiedFrom` into the result.
|
|
32
|
+
|
|
33
|
+
@example
|
|
34
|
+
```
|
|
35
|
+
type A = BuildObject<'a', string>;
|
|
36
|
+
//=> {a: string}
|
|
37
|
+
|
|
38
|
+
// Copy `readonly` and `?` from the key `a` of `{readonly a?: any}`
|
|
39
|
+
type B = BuildObject<'a', string, {readonly a?: any}>;
|
|
40
|
+
//=> {readonly a?: string}
|
|
41
|
+
```
|
|
42
|
+
*/
|
|
43
|
+
export type BuildObject<Key extends PropertyKey, Value, CopiedFrom extends UnknownRecord = {}> =
|
|
44
|
+
Key extends keyof CopiedFrom
|
|
45
|
+
? Pick<{[_ in keyof CopiedFrom]: Value}, Key>
|
|
46
|
+
: Key extends `${infer NumberKey extends number}`
|
|
47
|
+
? NumberKey extends keyof CopiedFrom
|
|
48
|
+
? Pick<{[_ in keyof CopiedFrom]: Value}, NumberKey>
|
|
49
|
+
: {[_ in Key]: Value}
|
|
50
|
+
: {[_ in Key]: Value};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
Return a string representation of the given string or number.
|
|
54
|
+
|
|
55
|
+
Note: This type is not the return type of the `.toString()` function.
|
|
56
|
+
*/
|
|
57
|
+
export type ToString<T> = T extends string | number ? `${T}` : never;
|
|
58
|
+
|
|
27
59
|
/**
|
|
28
60
|
Create a tuple of length `A` and a tuple composed of two other tuples,
|
|
29
61
|
the inferred tuple `U` and a tuple of length `B`, then extracts the length of tuple `U`.
|
|
@@ -13,16 +13,26 @@ const array = ['foo', 2];
|
|
|
13
13
|
|
|
14
14
|
typeof lastOf(array);
|
|
15
15
|
//=> number
|
|
16
|
+
|
|
17
|
+
const array = ['foo', 2] as const;
|
|
18
|
+
|
|
19
|
+
typeof lastOf(array);
|
|
20
|
+
//=> 2
|
|
16
21
|
```
|
|
17
22
|
|
|
18
23
|
@category Array
|
|
19
24
|
@category Template literal
|
|
20
25
|
*/
|
|
21
|
-
export type LastArrayElement<Elements extends readonly unknown[]>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
export type LastArrayElement<Elements extends readonly unknown[], ElementBeforeTailingSpreadElement = never> =
|
|
27
|
+
// If the last element of an array is a spread element, the `LastArrayElement` result should be `'the type of the element before the spread element' | 'the type of the spread element'`.
|
|
28
|
+
Elements extends readonly []
|
|
29
|
+
? ElementBeforeTailingSpreadElement
|
|
30
|
+
: Elements extends readonly [...infer U, infer V]
|
|
31
|
+
? V
|
|
32
|
+
: Elements extends readonly [infer U, ...infer V]
|
|
33
|
+
// If we return `V[number] | U` directly, it would be wrong for `[[string, boolean, object, ...number[]]`.
|
|
34
|
+
// So we need to recurse type `V` and carry over the type of the element before the spread element.
|
|
35
|
+
? LastArrayElement<V, U>
|
|
36
|
+
: Elements extends ReadonlyArray<infer U>
|
|
37
|
+
? U | ElementBeforeTailingSpreadElement
|
|
38
|
+
: never;
|
package/source/partial-deep.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {BuiltIns} from './internal';
|
|
1
|
+
import type {BuiltIns, UnknownRecord} from './internal';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
@see PartialDeep
|
|
@@ -59,7 +59,7 @@ const partialSettings: PartialDeep<Settings, {recurseIntoArrays: true}> = {
|
|
|
59
59
|
@category Set
|
|
60
60
|
@category Map
|
|
61
61
|
*/
|
|
62
|
-
export type PartialDeep<T, Options extends PartialDeepOptions = {}> = T extends BuiltIns
|
|
62
|
+
export type PartialDeep<T, Options extends PartialDeepOptions = {}> = T extends BuiltIns | (((...arguments_: any[]) => unknown)) | (new (...arguments_: any[]) => unknown)
|
|
63
63
|
? T
|
|
64
64
|
: T extends Map<infer KeyType, infer ValueType>
|
|
65
65
|
? PartialMapDeep<KeyType, ValueType, Options>
|
|
@@ -69,19 +69,17 @@ export type PartialDeep<T, Options extends PartialDeepOptions = {}> = T extends
|
|
|
69
69
|
? PartialReadonlyMapDeep<KeyType, ValueType, Options>
|
|
70
70
|
: T extends ReadonlySet<infer ItemType>
|
|
71
71
|
? PartialReadonlySetDeep<ItemType, Options>
|
|
72
|
-
: T extends
|
|
73
|
-
? T
|
|
74
|
-
: T extends
|
|
75
|
-
?
|
|
76
|
-
?
|
|
77
|
-
? ItemType[] extends T //
|
|
78
|
-
?
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
: PartialObjectDeep<T, Options>
|
|
84
|
-
: unknown;
|
|
72
|
+
: T extends UnknownRecord
|
|
73
|
+
? PartialObjectDeep<T, Options>
|
|
74
|
+
: T extends ReadonlyArray<infer ItemType> // Test for arrays/tuples, per https://github.com/microsoft/TypeScript/issues/35156
|
|
75
|
+
? Options['recurseIntoArrays'] extends true
|
|
76
|
+
? ItemType[] extends T // Test for arrays (non-tuples) specifically
|
|
77
|
+
? readonly ItemType[] extends T // Differentiate readonly and mutable arrays
|
|
78
|
+
? ReadonlyArray<PartialDeep<ItemType | undefined, Options>>
|
|
79
|
+
: Array<PartialDeep<ItemType | undefined, Options>>
|
|
80
|
+
: PartialObjectDeep<T, Options> // Tuples behave properly
|
|
81
|
+
: T // If they don't opt into array testing, just use the original type
|
|
82
|
+
: T;
|
|
85
83
|
|
|
86
84
|
/**
|
|
87
85
|
Same as `PartialDeep`, but accepts only `Map`s and as inputs. Internal helper for `PartialDeep`.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type {ToString} from './internal';
|
|
2
|
+
import type {EmptyObject} from './empty-object';
|
|
3
|
+
import type {IsAny} from './is-any';
|
|
4
|
+
import type {IsNever} from './is-never';
|
|
5
|
+
import type {UnknownArray} from './unknown-array';
|
|
6
|
+
import type {UnknownRecord} from './unknown-record';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
Return the part of the given array with a fixed index.
|
|
10
|
+
|
|
11
|
+
@example
|
|
12
|
+
```
|
|
13
|
+
type A = [string, number, boolean, ...string[]];
|
|
14
|
+
type B = FilterFixedIndexArray<A>;
|
|
15
|
+
//=> [string, number, boolean]
|
|
16
|
+
```
|
|
17
|
+
*/
|
|
18
|
+
type FilterFixedIndexArray<T extends UnknownArray, Result extends UnknownArray = []> =
|
|
19
|
+
number extends T['length'] ?
|
|
20
|
+
T extends readonly [infer U, ...infer V]
|
|
21
|
+
? FilterFixedIndexArray<V, [...Result, U]>
|
|
22
|
+
: Result
|
|
23
|
+
: T;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
Return the part of the given array with a non-fixed index.
|
|
27
|
+
|
|
28
|
+
@example
|
|
29
|
+
```
|
|
30
|
+
type A = [string, number, boolean, ...string[]];
|
|
31
|
+
type B = FilterNotFixedIndexArray<A>;
|
|
32
|
+
//=> string[]
|
|
33
|
+
```
|
|
34
|
+
*/
|
|
35
|
+
type FilterNotFixedIndexArray<T extends UnknownArray> =
|
|
36
|
+
T extends readonly [...FilterFixedIndexArray<T>, ...infer U]
|
|
37
|
+
? U
|
|
38
|
+
: [];
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
Generate a union of all possible paths to properties in the given object.
|
|
42
|
+
|
|
43
|
+
It also works with arrays.
|
|
44
|
+
|
|
45
|
+
Use-case: You want a type-safe way to access deeply nested properties in an object.
|
|
46
|
+
|
|
47
|
+
@example
|
|
48
|
+
```
|
|
49
|
+
import type {Paths} from 'type-fest';
|
|
50
|
+
|
|
51
|
+
type Project = {
|
|
52
|
+
filename: string;
|
|
53
|
+
listA: string[];
|
|
54
|
+
listB: [{filename: string}];
|
|
55
|
+
folder: {
|
|
56
|
+
subfolder: {
|
|
57
|
+
filename: string;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
type ProjectPaths = Paths<Project>;
|
|
63
|
+
//=> 'filename' | 'listA' | 'listB' | 'folder' | `listA.${number}` | 'listB.0' | 'listB.0.filename' | 'folder.subfolder' | 'folder.subfolder.filename'
|
|
64
|
+
|
|
65
|
+
declare function open<Path extends ProjectPaths>(path: Path): void;
|
|
66
|
+
|
|
67
|
+
open('filename'); // Pass
|
|
68
|
+
open('folder.subfolder'); // Pass
|
|
69
|
+
open('folder.subfolder.filename'); // Pass
|
|
70
|
+
open('foo'); // TypeError
|
|
71
|
+
|
|
72
|
+
// Also works with arrays
|
|
73
|
+
open('listA.1'); // Pass
|
|
74
|
+
open('listB.0'); // Pass
|
|
75
|
+
open('listB.1'); // TypeError. Because listB only has one element.
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
@category Object
|
|
79
|
+
@category Array
|
|
80
|
+
*/
|
|
81
|
+
export type Paths<T extends UnknownRecord | UnknownArray> =
|
|
82
|
+
IsAny<T> extends true
|
|
83
|
+
? never
|
|
84
|
+
: T extends UnknownArray
|
|
85
|
+
? number extends T['length']
|
|
86
|
+
// We need to handle the fixed and non-fixed index part of the array separately.
|
|
87
|
+
? InternalPaths<FilterFixedIndexArray<T>>
|
|
88
|
+
| InternalPaths<Array<FilterNotFixedIndexArray<T>[number]>>
|
|
89
|
+
: InternalPaths<T>
|
|
90
|
+
: InternalPaths<T>;
|
|
91
|
+
|
|
92
|
+
export type InternalPaths<_T extends UnknownRecord | UnknownArray, T = Required<_T>> =
|
|
93
|
+
T extends EmptyObject | readonly []
|
|
94
|
+
? never
|
|
95
|
+
: {
|
|
96
|
+
[Key in keyof T]:
|
|
97
|
+
Key extends string | number // Limit `Key` to string or number.
|
|
98
|
+
? T[Key] extends UnknownRecord | UnknownArray
|
|
99
|
+
? (
|
|
100
|
+
IsNever<Paths<T[Key]>> extends false
|
|
101
|
+
// If `Key` is a number, return `Key | `${Key}``, because both `array[0]` and `array['0']` work.
|
|
102
|
+
? Key | ToString<Key> | `${Key}.${Paths<T[Key]>}`
|
|
103
|
+
: Key | ToString<Key>
|
|
104
|
+
)
|
|
105
|
+
: Key | ToString<Key>
|
|
106
|
+
: never
|
|
107
|
+
}[keyof T & (T extends UnknownArray ? number : unknown)];
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type {BuildObject, BuildTuple, ToString} from './internal';
|
|
2
|
+
import type {Paths} from './paths';
|
|
3
|
+
import type {Simplify} from './simplify.d';
|
|
4
|
+
import type {UnionToIntersection} from './union-to-intersection.d';
|
|
5
|
+
import type {UnknownArray} from './unknown-array';
|
|
6
|
+
import type {UnknownRecord} from './unknown-record.d';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
Pick properties from a deeply-nested object.
|
|
10
|
+
|
|
11
|
+
It supports recursing into arrays.
|
|
12
|
+
|
|
13
|
+
Use-case: Distill complex objects down to the components you need to target.
|
|
14
|
+
|
|
15
|
+
@example
|
|
16
|
+
```
|
|
17
|
+
import type {PickDeep, PartialDeep} from 'type-fest';
|
|
18
|
+
|
|
19
|
+
type Configuration = {
|
|
20
|
+
userConfig: {
|
|
21
|
+
name: string;
|
|
22
|
+
age: number;
|
|
23
|
+
address: [
|
|
24
|
+
{
|
|
25
|
+
city1: string;
|
|
26
|
+
street1: string;
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
city2: string;
|
|
30
|
+
street2: string;
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
};
|
|
34
|
+
otherConfig: any;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
type NameConfig = PickDeep<Configuration, 'userConfig.name'>;
|
|
38
|
+
// type NameConfig = {
|
|
39
|
+
// userConfig: {
|
|
40
|
+
// name: string;
|
|
41
|
+
// };
|
|
42
|
+
|
|
43
|
+
// Supports optional properties
|
|
44
|
+
type User = PickDeep<PartialDeep<Configuration>, 'userConfig.name' | 'userConfig.age'>;
|
|
45
|
+
// type User = {
|
|
46
|
+
// userConfig?: {
|
|
47
|
+
// name?: string;
|
|
48
|
+
// age?: number;
|
|
49
|
+
// };
|
|
50
|
+
// };
|
|
51
|
+
|
|
52
|
+
// Supports array
|
|
53
|
+
type AddressConfig = PickDeep<Configuration, `userConfig.address.0`>;
|
|
54
|
+
// type AddressConfig = {
|
|
55
|
+
// userConfig: {
|
|
56
|
+
// address: [{
|
|
57
|
+
// city1: string;
|
|
58
|
+
// street1: string;
|
|
59
|
+
// }];
|
|
60
|
+
// };
|
|
61
|
+
// }
|
|
62
|
+
|
|
63
|
+
// Supports recurse into array
|
|
64
|
+
type Street = PickDeep<Configuration, `userConfig.address.1.street2`>;
|
|
65
|
+
// type AddressConfig = {
|
|
66
|
+
// userConfig: {
|
|
67
|
+
// address: [
|
|
68
|
+
// unknown,
|
|
69
|
+
// {street2: string}
|
|
70
|
+
// ];
|
|
71
|
+
// };
|
|
72
|
+
// }
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
@category Object
|
|
76
|
+
@category Array
|
|
77
|
+
*/
|
|
78
|
+
export type PickDeep<T extends UnknownRecord | UnknownArray, PathUnion extends Paths<T>> =
|
|
79
|
+
T extends UnknownRecord
|
|
80
|
+
? Simplify<UnionToIntersection<{
|
|
81
|
+
[P in PathUnion]: InternalPickDeep<T, P>;
|
|
82
|
+
}[PathUnion]>>
|
|
83
|
+
: T extends UnknownArray
|
|
84
|
+
? UnionToIntersection<{
|
|
85
|
+
[P in PathUnion]: InternalPickDeep<T, P>;
|
|
86
|
+
}[PathUnion]
|
|
87
|
+
>
|
|
88
|
+
: never;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
Pick an object/array from the given object/array by one path.
|
|
92
|
+
*/
|
|
93
|
+
type InternalPickDeep<
|
|
94
|
+
T extends UnknownRecord | UnknownArray,
|
|
95
|
+
Path extends string | number, // Checked paths, extracted from unchecked paths
|
|
96
|
+
> =
|
|
97
|
+
T extends UnknownArray ? PickDeepArray<T, Path>
|
|
98
|
+
: T extends UnknownRecord ? Simplify<PickDeepObject<T, Path>>
|
|
99
|
+
: never;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
Pick an object from the given object by one path.
|
|
103
|
+
*/
|
|
104
|
+
type PickDeepObject<RecordType extends UnknownRecord, P extends string | number> =
|
|
105
|
+
P extends `${infer RecordKeyInPath}.${infer SubPath}`
|
|
106
|
+
? BuildObject<RecordKeyInPath, InternalPickDeep<NonNullable<RecordType[RecordKeyInPath]>, SubPath>, RecordType>
|
|
107
|
+
: P extends keyof RecordType | ToString<keyof RecordType> // Handle number keys
|
|
108
|
+
? BuildObject<P, RecordType[P], RecordType>
|
|
109
|
+
: never;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
Pick an array from the given array by one path.
|
|
113
|
+
*/
|
|
114
|
+
type PickDeepArray<ArrayType extends UnknownArray, P extends string | number> =
|
|
115
|
+
// Handle paths that are `${number}.${string}`
|
|
116
|
+
P extends `${infer ArrayIndex extends number}.${infer SubPath}`
|
|
117
|
+
// When `ArrayIndex` is equal to `number`
|
|
118
|
+
? number extends ArrayIndex
|
|
119
|
+
? ArrayType extends unknown[]
|
|
120
|
+
? Array<InternalPickDeep<NonNullable<ArrayType[number]>, SubPath>>
|
|
121
|
+
: ArrayType extends readonly unknown[]
|
|
122
|
+
? ReadonlyArray<InternalPickDeep<NonNullable<ArrayType[number]>, SubPath>>
|
|
123
|
+
: never
|
|
124
|
+
// When `ArrayIndex` is a number literal
|
|
125
|
+
: ArrayType extends unknown[]
|
|
126
|
+
? [...BuildTuple<ArrayIndex>, InternalPickDeep<NonNullable<ArrayType[ArrayIndex]>, SubPath>]
|
|
127
|
+
: ArrayType extends readonly unknown[]
|
|
128
|
+
? readonly [...BuildTuple<ArrayIndex>, InternalPickDeep<NonNullable<ArrayType[ArrayIndex]>, SubPath>]
|
|
129
|
+
: never
|
|
130
|
+
// When the path is equal to `number`
|
|
131
|
+
: P extends `${infer ArrayIndex extends number}`
|
|
132
|
+
// When `ArrayIndex` is `number`
|
|
133
|
+
? number extends ArrayIndex
|
|
134
|
+
? ArrayType
|
|
135
|
+
// When `ArrayIndex` is a number literal
|
|
136
|
+
: ArrayType extends unknown[]
|
|
137
|
+
? [...BuildTuple<ArrayIndex>, ArrayType[ArrayIndex]]
|
|
138
|
+
: ArrayType extends readonly unknown[]
|
|
139
|
+
? readonly [...BuildTuple<ArrayIndex>, ArrayType[ArrayIndex]]
|
|
140
|
+
: never
|
|
141
|
+
: never;
|
|
@@ -38,28 +38,30 @@ Note that types containing overloaded functions are not made deeply readonly due
|
|
|
38
38
|
*/
|
|
39
39
|
export type ReadonlyDeep<T> = T extends BuiltIns
|
|
40
40
|
? T
|
|
41
|
-
: T extends (...
|
|
42
|
-
?
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
: T extends new (...args: any[]) => unknown
|
|
42
|
+
? T // Skip class constructors
|
|
43
|
+
: T extends (...arguments_: any[]) => unknown
|
|
44
|
+
? {} extends ReadonlyObjectDeep<T>
|
|
45
45
|
? T
|
|
46
|
-
:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
: T extends Readonly<
|
|
50
|
-
?
|
|
51
|
-
:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
? readonly [
|
|
56
|
-
: T extends readonly [
|
|
57
|
-
? readonly [
|
|
58
|
-
: T extends
|
|
59
|
-
?
|
|
60
|
-
: T extends
|
|
61
|
-
?
|
|
62
|
-
:
|
|
46
|
+
: HasMultipleCallSignatures<T> extends true
|
|
47
|
+
? T
|
|
48
|
+
: ((...arguments_: Parameters<T>) => ReturnType<T>) & ReadonlyObjectDeep<T>
|
|
49
|
+
: T extends Readonly<ReadonlyMap<infer KeyType, infer ValueType>>
|
|
50
|
+
? ReadonlyMapDeep<KeyType, ValueType>
|
|
51
|
+
: T extends Readonly<ReadonlySet<infer ItemType>>
|
|
52
|
+
? ReadonlySetDeep<ItemType>
|
|
53
|
+
: // Identify tuples to avoid converting them to arrays inadvertently; special case `readonly [...never[]]`, as it emerges undesirably from recursive invocations of ReadonlyDeep below.
|
|
54
|
+
T extends readonly [] | readonly [...never[]]
|
|
55
|
+
? readonly []
|
|
56
|
+
: T extends readonly [infer U, ...infer V]
|
|
57
|
+
? readonly [ReadonlyDeep<U>, ...ReadonlyDeep<V>]
|
|
58
|
+
: T extends readonly [...infer U, infer V]
|
|
59
|
+
? readonly [...ReadonlyDeep<U>, ReadonlyDeep<V>]
|
|
60
|
+
: T extends ReadonlyArray<infer ItemType>
|
|
61
|
+
? ReadonlyArray<ReadonlyDeep<ItemType>>
|
|
62
|
+
: T extends object
|
|
63
|
+
? ReadonlyObjectDeep<T>
|
|
64
|
+
: unknown;
|
|
63
65
|
|
|
64
66
|
/**
|
|
65
67
|
Same as `ReadonlyDeep`, but accepts only `ReadonlyMap`s as inputs. Internal helper for `ReadonlyDeep`.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type {Except} from './except';
|
|
2
|
+
import type {Simplify} from './simplify';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
Create a type that changes the type of the given keys.
|
|
6
|
+
|
|
7
|
+
Use-cases:
|
|
8
|
+
- Creating variations of a base model.
|
|
9
|
+
- Fixing incorrect external types.
|
|
10
|
+
|
|
11
|
+
@see `Merge` if you need to change multiple properties to different types.
|
|
12
|
+
|
|
13
|
+
@example
|
|
14
|
+
```
|
|
15
|
+
import type {SetFieldType} from 'type-fest';
|
|
16
|
+
|
|
17
|
+
type MyModel = {
|
|
18
|
+
id: number;
|
|
19
|
+
createdAt: Date;
|
|
20
|
+
updatedAt: Date;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type MyModelApi = SetFieldType<MyModel, 'createdAt' | 'updatedAt', string>;
|
|
24
|
+
// {
|
|
25
|
+
// id: number;
|
|
26
|
+
// createdAt: string;
|
|
27
|
+
// updatedAt: string;
|
|
28
|
+
// }
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
@category Object
|
|
32
|
+
*/
|
|
33
|
+
export type SetFieldType<BaseType, Keys extends keyof BaseType, NewType> =
|
|
34
|
+
Simplify<
|
|
35
|
+
Except<BaseType, Keys> &
|
|
36
|
+
Record<Keys, NewType>
|
|
37
|
+
>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type {IsUnknown} from './is-unknown';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
Create a tuple that replaces the given `Tuple`'s elements with the given `Record_`'s values at the given indices.
|
|
5
|
+
*/
|
|
6
|
+
type MergeObjectToTuple<Tuple extends unknown[], Record_ extends Record<number, unknown>> = {
|
|
7
|
+
[K in keyof Tuple]: Record_ extends Record<string, unknown>
|
|
8
|
+
// Handle object case like `{0: string, 1: number}`
|
|
9
|
+
? K extends `${infer NumberK extends number}`
|
|
10
|
+
? NumberK extends keyof Record_ ? Record_[K] : Tuple[K]
|
|
11
|
+
: never // Should not happen
|
|
12
|
+
// Handle tuple case like `[string, number]`
|
|
13
|
+
: K extends keyof Record_ ? Record_[K] : Tuple[K]
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
Create a function that replaces some parameters with the given parameters.
|
|
18
|
+
|
|
19
|
+
The parameters that are not specified will be kept as-is.
|
|
20
|
+
|
|
21
|
+
Note:
|
|
22
|
+
- This type will ignore the given function's generic type.
|
|
23
|
+
- If you change the parameter type that return type depends on, the return type will not change:
|
|
24
|
+
```
|
|
25
|
+
const fn = (a: number) => a;
|
|
26
|
+
//=> fn: (a: number) => number;
|
|
27
|
+
|
|
28
|
+
// We change type of `a` to `string`, but return type is still `number`.
|
|
29
|
+
type Fn = SetParameterType<typeof fn, {0: string}>;
|
|
30
|
+
//=> (a: string) => number;
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Use-case:
|
|
34
|
+
- Define a wrapped function that receives something different while returning the same type.
|
|
35
|
+
- Mocking and testing.
|
|
36
|
+
- Overload function type. (See example)
|
|
37
|
+
|
|
38
|
+
@example
|
|
39
|
+
```
|
|
40
|
+
import type {SetParameterType} from 'type-fest';
|
|
41
|
+
|
|
42
|
+
type HandleMessage = (data: Data, message: string) => void;
|
|
43
|
+
|
|
44
|
+
type HandleOk = SetParameterType<HandleMessage, {0: SuccessData, 1: 'ok'}>;
|
|
45
|
+
//=> type HandleOk = (data: SuccessData, message: 'ok') => void;
|
|
46
|
+
|
|
47
|
+
// Another way to define the parameters to replace.
|
|
48
|
+
type HandleError = SetParameterType<HandleMessage, [data: ErrorData, message: 'error']>;
|
|
49
|
+
//=> type HandleError = (data: ErrorData, message: 'error') => void;
|
|
50
|
+
|
|
51
|
+
// Could change single parameter type.
|
|
52
|
+
type HandleWarn = SetParameterType<HandleMessage, {1: 'warn'}>;
|
|
53
|
+
//=> type HandleWarn = (data: Data, message: 'warn') => void;
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
@category Function
|
|
57
|
+
*/
|
|
58
|
+
export type SetParameterType<Fn extends (...arguments_: any[]) => unknown, P extends Record<number, unknown>> =
|
|
59
|
+
// Just using `Parameters<Fn>` isn't ideal because it doesn't handle the `this` fake parameter.
|
|
60
|
+
Fn extends (this: infer ThisArg, ...arguments_: infer Arguments) => unknown
|
|
61
|
+
? (
|
|
62
|
+
// If a function did not specify the `this` fake parameter, it will be inferred to `unknown`.
|
|
63
|
+
// We want to detect this situation just to display a friendlier type upon hovering on an IntelliSense-powered IDE.
|
|
64
|
+
IsUnknown<ThisArg> extends true
|
|
65
|
+
? (...arguments_: MergeObjectToTuple<Arguments, P>) => ReturnType<Fn>
|
|
66
|
+
: (this: ThisArg, ...arguments_: MergeObjectToTuple<Arguments, P>) => ReturnType<Fn>
|
|
67
|
+
)
|
|
68
|
+
: Fn; // This part should be unreachable
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Represents an array with `unknown` value.
|
|
3
|
+
|
|
4
|
+
Use case: You want a type that all arrays can be assigned to, but you don't care about the value.
|
|
5
|
+
|
|
6
|
+
@example
|
|
7
|
+
```
|
|
8
|
+
import type {UnknownArray} from 'type-fest';
|
|
9
|
+
|
|
10
|
+
type IsArray<T> = T extends UnknownArray ? true : false;
|
|
11
|
+
|
|
12
|
+
type A = IsArray<['foo']>;
|
|
13
|
+
//=> true
|
|
14
|
+
|
|
15
|
+
type B = IsArray<readonly number[]>;
|
|
16
|
+
//=> true
|
|
17
|
+
|
|
18
|
+
type C = IsArray<string>;
|
|
19
|
+
//=> false
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
@category Type
|
|
23
|
+
@category Array
|
|
24
|
+
*/
|
|
25
|
+
export type UnknownArray = readonly unknown[];
|
|
@@ -38,23 +38,31 @@ export type WritableDeep<T> = T extends BuiltIns
|
|
|
38
38
|
: HasMultipleCallSignatures<T> extends true
|
|
39
39
|
? T
|
|
40
40
|
: ((...arguments_: Parameters<T>) => ReturnType<T>) & WritableObjectDeep<T>
|
|
41
|
-
: T extends
|
|
42
|
-
? WritableMapDeep<
|
|
43
|
-
: T extends
|
|
44
|
-
? WritableSetDeep<
|
|
45
|
-
: T extends
|
|
46
|
-
?
|
|
47
|
-
:
|
|
41
|
+
: T extends ReadonlyMap<unknown, unknown>
|
|
42
|
+
? WritableMapDeep<T>
|
|
43
|
+
: T extends ReadonlySet<unknown>
|
|
44
|
+
? WritableSetDeep<T>
|
|
45
|
+
: T extends readonly unknown[]
|
|
46
|
+
? WritableArrayDeep<T>
|
|
47
|
+
: T extends object
|
|
48
|
+
? WritableObjectDeep<T>
|
|
49
|
+
: unknown;
|
|
48
50
|
|
|
49
51
|
/**
|
|
50
52
|
Same as `WritableDeep`, but accepts only `Map`s as inputs. Internal helper for `WritableDeep`.
|
|
51
53
|
*/
|
|
52
|
-
type WritableMapDeep<
|
|
54
|
+
type WritableMapDeep<MapType extends ReadonlyMap<unknown, unknown>> =
|
|
55
|
+
MapType extends ReadonlyMap<infer KeyType, infer ValueType>
|
|
56
|
+
? Map<WritableDeep<KeyType>, WritableDeep<ValueType>>
|
|
57
|
+
: MapType; // Should not heppen
|
|
53
58
|
|
|
54
59
|
/**
|
|
55
60
|
Same as `WritableDeep`, but accepts only `Set`s as inputs. Internal helper for `WritableDeep`.
|
|
56
61
|
*/
|
|
57
|
-
type WritableSetDeep<
|
|
62
|
+
type WritableSetDeep<SetType extends ReadonlySet<unknown>> =
|
|
63
|
+
SetType extends ReadonlySet<infer ItemType>
|
|
64
|
+
? Set<WritableDeep<ItemType>>
|
|
65
|
+
: SetType; // Should not heppen
|
|
58
66
|
|
|
59
67
|
/**
|
|
60
68
|
Same as `WritableDeep`, but accepts only `object`s as inputs. Internal helper for `WritableDeep`.
|
|
@@ -62,3 +70,15 @@ Same as `WritableDeep`, but accepts only `object`s as inputs. Internal helper fo
|
|
|
62
70
|
type WritableObjectDeep<ObjectType extends object> = {
|
|
63
71
|
-readonly [KeyType in keyof ObjectType]: WritableDeep<ObjectType[KeyType]>
|
|
64
72
|
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
Same as `WritableDeep`, but accepts only `Array`s as inputs. Internal helper for `WritableDeep`.
|
|
76
|
+
*/
|
|
77
|
+
type WritableArrayDeep<ArrayType extends readonly unknown[]> =
|
|
78
|
+
ArrayType extends readonly [] ? []
|
|
79
|
+
: ArrayType extends readonly [...infer U, infer V] ? [...WritableArrayDeep<U>, WritableDeep<V>]
|
|
80
|
+
: ArrayType extends readonly [infer U, ...infer V] ? [WritableDeep<U>, ...WritableArrayDeep<V>]
|
|
81
|
+
: ArrayType extends ReadonlyArray<infer U> ? Array<WritableDeep<U>>
|
|
82
|
+
: ArrayType extends Array<infer U> ? Array<WritableDeep<U>>
|
|
83
|
+
: ArrayType;
|
|
84
|
+
|
package/source/writable.d.ts
CHANGED
|
@@ -2,7 +2,21 @@ import type {Except} from './except';
|
|
|
2
2
|
import type {Simplify} from './simplify';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
Create a
|
|
5
|
+
Create a writable version of the given array type.
|
|
6
|
+
*/
|
|
7
|
+
type WritableArray<ArrayType extends readonly unknown[]> =
|
|
8
|
+
ArrayType extends readonly [] ? []
|
|
9
|
+
: ArrayType extends readonly [...infer U, infer V] ? [...U, V]
|
|
10
|
+
: ArrayType extends readonly [infer U, ...infer V] ? [U, ...V]
|
|
11
|
+
: ArrayType extends ReadonlyArray<infer U> ? U[]
|
|
12
|
+
: ArrayType;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
Create a type that strips `readonly` from the given type. Inverse of `Readonly<T>`.
|
|
16
|
+
|
|
17
|
+
The 2nd argument will be ignored if the input type is not an object.
|
|
18
|
+
|
|
19
|
+
Note: This type can make readonly `Set` and `Map` writable. This behavior is different from `Readonly<T>` (as of TypeScript 5.2.2). See: https://github.com/microsoft/TypeScript/issues/29655
|
|
6
20
|
|
|
7
21
|
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), [construct a `readonly` object within a function](https://github.com/Microsoft/TypeScript/issues/24509), or to define a single model where the only thing that changes is whether or not some of the keys are writable.
|
|
8
22
|
|
|
@@ -27,14 +41,28 @@ type SomeWritable = Writable<Foo, 'b' | 'c'>;
|
|
|
27
41
|
// b: readonly string[]; // It's now writable. The type of the property remains unaffected.
|
|
28
42
|
// c: boolean; // It's now writable.
|
|
29
43
|
// }
|
|
44
|
+
|
|
45
|
+
// Also supports array
|
|
46
|
+
const readonlyArray: readonly number[] = [1, 2, 3];
|
|
47
|
+
readonlyArray.push(4); // Will fail as the array itself is readonly.
|
|
48
|
+
const writableArray: Writable<typeof readonlyArray> = readonlyArray as Writable<typeof readonlyArray>;
|
|
49
|
+
writableArray.push(4); // Will work as the array itself is now writable.
|
|
30
50
|
```
|
|
31
51
|
|
|
32
52
|
@category Object
|
|
33
53
|
*/
|
|
34
54
|
export type Writable<BaseType, Keys extends keyof BaseType = keyof BaseType> =
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
55
|
+
BaseType extends ReadonlyMap<infer KeyType, infer ValueType>
|
|
56
|
+
? Map<KeyType, ValueType>
|
|
57
|
+
: BaseType extends ReadonlySet<infer ItemType>
|
|
58
|
+
? Set<ItemType>
|
|
59
|
+
: BaseType extends readonly unknown[]
|
|
60
|
+
// Handle array
|
|
61
|
+
? WritableArray<BaseType>
|
|
62
|
+
// Handle object
|
|
63
|
+
: Simplify<
|
|
64
|
+
// Pick just the keys that are not writable from the base type.
|
|
65
|
+
Except<BaseType, Keys> &
|
|
66
|
+
// Pick the keys that should be writable from the base type and make them writable by removing the `readonly` modifier from the key.
|
|
67
|
+
{-readonly [KeyType in keyof Pick<BaseType, Keys>]: Pick<BaseType, Keys>[KeyType]}
|
|
68
|
+
>;
|