type-fest 4.6.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 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';
@@ -101,6 +103,8 @@ export type {IsUnknown} from './source/is-unknown';
101
103
  export type {IfUnknown} from './source/if-unknown';
102
104
  export type {ArrayIndices} from './source/array-indices';
103
105
  export type {ArrayValues} from './source/array-values';
106
+ export type {SetFieldType} from './source/set-field-type';
107
+ export type {Paths} from './source/paths';
104
108
 
105
109
  // Template literal types
106
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.6.0",
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 all or some of an object's keys. The inverse of `Readonly<T>`.
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.
@@ -175,6 +177,8 @@ Click the type names for complete docs.
175
177
  - [`IntRange`](source/int-range.d.ts) - Generate a union of numbers.
176
178
  - [`ArrayIndices`](source/array-indices.d.ts) - Provides valid indices for a constant array or tuple.
177
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.
178
182
 
179
183
  ### Type Guard
180
184
 
@@ -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 object
100
+ : Type[Key] extends UnknownRecord
99
101
  ? ConditionalPickDeep<Type[Key], Condition, Options>
100
102
  : typeof conditionalPickDeepSymbol;
101
- }, (typeof conditionalPickDeepSymbol | undefined) | Record<PropertyKey, never>>>;
103
+ }, (typeof conditionalPickDeepSymbol | undefined) | EmptyObject>, never, UnknownRecord>;
@@ -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`.
@@ -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
@@ -69,8 +69,9 @@ 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 object
73
- ? T extends ReadonlyArray<infer ItemType> // Test for arrays/tuples, per https://github.com/microsoft/TypeScript/issues/35156
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
74
75
  ? Options['recurseIntoArrays'] extends true
75
76
  ? ItemType[] extends T // Test for arrays (non-tuples) specifically
76
77
  ? readonly ItemType[] extends T // Differentiate readonly and mutable arrays
@@ -78,8 +79,7 @@ export type PartialDeep<T, Options extends PartialDeepOptions = {}> = T extends
78
79
  : Array<PartialDeep<ItemType | undefined, Options>>
79
80
  : PartialObjectDeep<T, Options> // Tuples behave properly
80
81
  : T // If they don't opt into array testing, just use the original type
81
- : PartialObjectDeep<T, Options>
82
- : unknown;
82
+ : T;
83
83
 
84
84
  /**
85
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;
@@ -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,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[];
@@ -2,7 +2,21 @@ import type {Except} from './except';
2
2
  import type {Simplify} from './simplify';
3
3
 
4
4
  /**
5
- Create a type that strips `readonly` from all or some of an object's keys. Inverse of `Readonly<T>`.
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
- Simplify<
36
- // Pick just the keys that are not writable from the base type.
37
- Except<BaseType, Keys> &
38
- // Pick the keys that should be writable from the base type and make them writable by removing the `readonly` modifier from the key.
39
- {-readonly [KeyType in keyof Pick<BaseType, Keys>]: Pick<BaseType, Keys>[KeyType]}
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
+ >;