type-fest 3.0.0 → 3.2.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 +2 -0
- package/package.json +4 -4
- package/readme.md +3 -1
- package/source/camel-case.d.ts +29 -24
- package/source/camel-cased-properties-deep.d.ts +5 -5
- package/source/camel-cased-properties.d.ts +3 -3
- package/source/delimiter-case.d.ts +2 -2
- package/source/enforce-optional.d.ts +1 -1
- package/source/fixed-length-array.d.ts +1 -1
- package/source/internal.d.ts +51 -0
- package/source/jsonifiable.d.ts +37 -0
- package/source/jsonify.d.ts +3 -2
- package/source/merge-deep.d.ts +470 -0
- package/source/observable-like.d.ts +1 -1
- package/source/package-json.d.ts +1 -1
- package/source/require-at-least-one.d.ts +0 -1
- package/source/schema.d.ts +0 -1
- package/source/set-non-nullable.d.ts +14 -5
- package/source/split-words.d.ts +57 -0
- package/source/stringified.d.ts +1 -1
- package/source/tuple-to-union.d.ts +2 -2
package/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export type {EmptyObject, IsEmptyObject} from './source/empty-object';
|
|
|
9
9
|
export type {Except} from './source/except';
|
|
10
10
|
export type {Writable} from './source/writable';
|
|
11
11
|
export type {Merge} from './source/merge';
|
|
12
|
+
export type {MergeDeep, MergeDeepOptions} from './source/merge-deep';
|
|
12
13
|
export type {MergeExclusive} from './source/merge-exclusive';
|
|
13
14
|
export type {RequireAtLeastOne} from './source/require-at-least-one';
|
|
14
15
|
export type {RequireExactlyOne} from './source/require-exactly-one';
|
|
@@ -49,6 +50,7 @@ export type {SetReturnType} from './source/set-return-type';
|
|
|
49
50
|
export type {Asyncify} from './source/asyncify';
|
|
50
51
|
export type {Simplify} from './source/simplify';
|
|
51
52
|
export type {Jsonify} from './source/jsonify';
|
|
53
|
+
export type {Jsonifiable} from './source/jsonifiable';
|
|
52
54
|
export type {Schema} from './source/schema';
|
|
53
55
|
export type {LiteralToPrimitive} from './source/literal-to-primitive';
|
|
54
56
|
export type {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "type-fest",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "A collection of essential TypeScript types",
|
|
5
5
|
"license": "(MIT OR CC0-1.0)",
|
|
6
6
|
"repository": "sindresorhus/type-fest",
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
],
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@sindresorhus/tsconfig": "~0.7.0",
|
|
38
|
-
"expect-type": "^0.
|
|
38
|
+
"expect-type": "^0.15.0",
|
|
39
39
|
"tsd": "^0.24.1",
|
|
40
|
-
"typescript": "^4.8.
|
|
41
|
-
"xo": "^0.52.
|
|
40
|
+
"typescript": "^4.8.4",
|
|
41
|
+
"xo": "^0.52.4"
|
|
42
42
|
},
|
|
43
43
|
"xo": {
|
|
44
44
|
"rules": {
|
package/readme.md
CHANGED
|
@@ -144,6 +144,7 @@ Click the type names for complete docs.
|
|
|
144
144
|
- [`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).
|
|
145
145
|
- [`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>`.
|
|
146
146
|
- [`Merge`](source/merge.d.ts) - Merge two types into a new type. Keys of the second type overrides keys of the first type.
|
|
147
|
+
- [`MergeDeep`](source/merge-deep.d.ts) - Merge two objects or two arrays/tuples recursively into a new type.
|
|
147
148
|
- [`MergeExclusive`](source/merge-exclusive.d.ts) - Create a type that has mutually exclusive keys.
|
|
148
149
|
- [`RequireAtLeastOne`](source/require-at-least-one.d.ts) - Create a type that requires at least one of the given keys.
|
|
149
150
|
- [`RequireExactlyOne`](source/require-exactly-one.d.ts) - Create a type that requires exactly a single key of the given keys and disallows more.
|
|
@@ -186,6 +187,7 @@ Click the type names for complete docs.
|
|
|
186
187
|
### JSON
|
|
187
188
|
|
|
188
189
|
- [`Jsonify`](source/jsonify.d.ts) - Transform a type to one that is assignable to the `JsonValue` type.
|
|
190
|
+
- [`Jsonifiable`](source/jsonifiable.d.ts) - Matches a value that can be losslessly converted to JSON.
|
|
189
191
|
- [`JsonPrimitive`](source/basic.d.ts) - Matches a JSON primitive.
|
|
190
192
|
- [`JsonObject`](source/basic.d.ts) - Matches a JSON object.
|
|
191
193
|
- [`JsonArray`](source/basic.d.ts) - Matches a JSON array.
|
|
@@ -212,7 +214,7 @@ Click the type names for complete docs.
|
|
|
212
214
|
- [`MultidimensionalArray`](source/multidimensional-array.d.ts) - Create a type that represents a multidimensional array of the given type and dimensions.
|
|
213
215
|
- [`MultidimensionalReadonlyArray`](source/multidimensional-readonly-array.d.ts) - Create a type that represents a multidimensional readonly array of the given type and dimensions.
|
|
214
216
|
- [`ReadonlyTuple`](source/readonly-tuple.d.ts) - Create a type that represents a read-only tuple of the given type and length.
|
|
215
|
-
- [`TupleToUnion`](source/tuple-to-union.d.ts) - Convert a tuple into a union type of its elements.
|
|
217
|
+
- [`TupleToUnion`](source/tuple-to-union.d.ts) - Convert a tuple/array into a union type of its elements.
|
|
216
218
|
|
|
217
219
|
### Numeric
|
|
218
220
|
|
package/source/camel-case.d.ts
CHANGED
|
@@ -1,39 +1,42 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {Split} from './split';
|
|
1
|
+
import type {SplitWords} from './split-words';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
|
-
|
|
4
|
+
CamelCase options.
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@see CamelCaseStringArray
|
|
6
|
+
@see {@link CamelCase}
|
|
10
7
|
*/
|
|
11
|
-
type
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
? ''
|
|
15
|
-
: FirstPart extends ''
|
|
16
|
-
? InnerCamelCaseStringArray<RemainingParts, PreviousPart>
|
|
17
|
-
: `${PreviousPart extends '' ? FirstPart : Capitalize<FirstPart>}${InnerCamelCaseStringArray<RemainingParts, FirstPart>}`
|
|
18
|
-
: '';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
Starts fusing the output of `Split<>`, an array literal of strings, into a camel-cased string literal.
|
|
8
|
+
export type CamelCaseOptions = {
|
|
9
|
+
/**
|
|
10
|
+
Whether to preserved consecutive uppercase letter.
|
|
22
11
|
|
|
23
|
-
|
|
12
|
+
@default true
|
|
13
|
+
*/
|
|
14
|
+
preserveConsecutiveUppercase?: boolean;
|
|
15
|
+
};
|
|
24
16
|
|
|
25
|
-
|
|
17
|
+
/**
|
|
18
|
+
Convert an array of words to camel-case.
|
|
26
19
|
*/
|
|
27
|
-
type
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
type CamelCaseFromArray<
|
|
21
|
+
Words extends string[],
|
|
22
|
+
Options extends CamelCaseOptions,
|
|
23
|
+
OutputString extends string = '',
|
|
24
|
+
> = Words extends [
|
|
25
|
+
infer FirstWord extends string,
|
|
26
|
+
...infer RemainingWords extends string[],
|
|
27
|
+
]
|
|
28
|
+
? Options['preserveConsecutiveUppercase'] extends true
|
|
29
|
+
? `${Capitalize<FirstWord>}${CamelCaseFromArray<RemainingWords, Options>}`
|
|
30
|
+
: `${Capitalize<Lowercase<FirstWord>>}${CamelCaseFromArray<RemainingWords, Options>}`
|
|
31
|
+
: OutputString;
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
Convert a string literal to camel-case.
|
|
34
35
|
|
|
35
36
|
This can be useful when, for example, converting some kebab-cased command-line flags or a snake-cased database result.
|
|
36
37
|
|
|
38
|
+
By default, consecutive uppercase letter are preserved. See {@link CamelCaseOptions.preserveConsecutiveUppercase preserveConsecutiveUppercase} option to change this behaviour.
|
|
39
|
+
|
|
37
40
|
@example
|
|
38
41
|
```
|
|
39
42
|
import type {CamelCase} from 'type-fest';
|
|
@@ -70,4 +73,6 @@ const dbResult: CamelCasedProperties<RawOptions> = {
|
|
|
70
73
|
@category Change case
|
|
71
74
|
@category Template literal
|
|
72
75
|
*/
|
|
73
|
-
export type CamelCase<
|
|
76
|
+
export type CamelCase<Type, Options extends CamelCaseOptions = {preserveConsecutiveUppercase: true}> = Type extends string
|
|
77
|
+
? Uncapitalize<CamelCaseFromArray<SplitWords<Type extends Uppercase<Type> ? Lowercase<Type> : Type>, Options>>
|
|
78
|
+
: Type;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {CamelCase} from './camel-case';
|
|
1
|
+
import type {CamelCase, CamelCaseOptions} from './camel-case';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
Convert object properties to camel case recursively.
|
|
@@ -44,11 +44,11 @@ const result: CamelCasedPropertiesDeep<UserWithFriends> = {
|
|
|
44
44
|
@category Template literal
|
|
45
45
|
@category Object
|
|
46
46
|
*/
|
|
47
|
-
export type CamelCasedPropertiesDeep<Value> = Value extends Function
|
|
47
|
+
export type CamelCasedPropertiesDeep<Value, Options extends CamelCaseOptions = {preserveConsecutiveUppercase: true}> = Value extends Function
|
|
48
48
|
? Value
|
|
49
49
|
: Value extends Array<infer U>
|
|
50
|
-
? Array<CamelCasedPropertiesDeep<U>>
|
|
50
|
+
? Array<CamelCasedPropertiesDeep<U, Options>>
|
|
51
51
|
: Value extends Set<infer U>
|
|
52
|
-
? Set<CamelCasedPropertiesDeep<U>> : {
|
|
53
|
-
[K in keyof Value as CamelCase<K>]: CamelCasedPropertiesDeep<Value[K]>;
|
|
52
|
+
? Set<CamelCasedPropertiesDeep<U, Options>> : {
|
|
53
|
+
[K in keyof Value as CamelCase<K, Options>]: CamelCasedPropertiesDeep<Value[K], Options>;
|
|
54
54
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {CamelCase} from './camel-case';
|
|
1
|
+
import type {CamelCase, CamelCaseOptions} from './camel-case';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
Convert object properties to camel case but not recursively.
|
|
@@ -27,10 +27,10 @@ const result: CamelCasedProperties<User> = {
|
|
|
27
27
|
@category Template literal
|
|
28
28
|
@category Object
|
|
29
29
|
*/
|
|
30
|
-
export type CamelCasedProperties<Value> = Value extends Function
|
|
30
|
+
export type CamelCasedProperties<Value, Options extends CamelCaseOptions = {preserveConsecutiveUppercase: true}> = Value extends Function
|
|
31
31
|
? Value
|
|
32
32
|
: Value extends Array<infer U>
|
|
33
33
|
? Value
|
|
34
34
|
: {
|
|
35
|
-
[K in keyof Value as CamelCase<K>]: Value[K];
|
|
35
|
+
[K in keyof Value as CamelCase<K, Options>]: Value[K];
|
|
36
36
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type {UpperCaseCharacters, WordSeparators} from '../source/internal';
|
|
2
2
|
|
|
3
|
-
// Transforms a string that is fully uppercase into a fully lowercase version. Needed to add support for
|
|
3
|
+
// Transforms a string that is fully uppercase into a fully lowercase version. Needed to add support for SCREAMING_SNAKE_CASE, see https://github.com/sindresorhus/type-fest/issues/385
|
|
4
4
|
type UpperCaseToLowerCase<T extends string> = T extends Uppercase<T> ? Lowercase<T> : T;
|
|
5
5
|
|
|
6
|
-
// This
|
|
6
|
+
// This implementation does not support SCREAMING_SNAKE_CASE, it is used internally by `SplitIncludingDelimiters`.
|
|
7
7
|
type SplitIncludingDelimiters_<Source extends string, Delimiter extends string> =
|
|
8
8
|
Source extends '' ? [] :
|
|
9
9
|
Source extends `${infer FirstPart}${Delimiter}${infer SecondPart}` ?
|
|
@@ -6,7 +6,7 @@ type ArrayLengthMutationKeys = 'splice' | 'push' | 'pop' | 'shift' | 'unshift';
|
|
|
6
6
|
/**
|
|
7
7
|
Create a type that represents an array of the given type and length. The array's length and the `Array` prototype methods that manipulate its length are excluded in the resulting type.
|
|
8
8
|
|
|
9
|
-
Please participate in [this issue](https://github.com/microsoft/TypeScript/issues/26223) if you want to have a
|
|
9
|
+
Please participate in [this issue](https://github.com/microsoft/TypeScript/issues/26223) if you want to have a similar type built into TypeScript.
|
|
10
10
|
|
|
11
11
|
Use-cases:
|
|
12
12
|
- Declaring fixed-length tuples or arrays with a large number of items.
|
package/source/internal.d.ts
CHANGED
|
@@ -57,3 +57,54 @@ export type UpperCaseCharacters = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H'
|
|
|
57
57
|
export type WordSeparators = '-' | '_' | ' ';
|
|
58
58
|
|
|
59
59
|
export type StringDigit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
Matches any unknown record.
|
|
63
|
+
*/
|
|
64
|
+
export type UnknownRecord = Record<PropertyKey, unknown>;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
Matches any unknown array or tuple.
|
|
68
|
+
*/
|
|
69
|
+
export type UnknownArrayOrTuple = readonly [...unknown[]];
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
Matches any non empty tuple.
|
|
73
|
+
*/
|
|
74
|
+
export type NonEmptyTuple = readonly [unknown, ...unknown[]];
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
Returns a boolean for whether the two given types extends the base type.
|
|
78
|
+
*/
|
|
79
|
+
export type IsBothExtends<BaseType, FirstType, SecondType> = FirstType extends BaseType
|
|
80
|
+
? SecondType extends BaseType
|
|
81
|
+
? true
|
|
82
|
+
: false
|
|
83
|
+
: false;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
Extracts the type of the first element of an array or tuple.
|
|
87
|
+
*/
|
|
88
|
+
export type FirstArrayElement<TArray extends UnknownArrayOrTuple> = TArray extends readonly [infer THead, ...unknown[]]
|
|
89
|
+
? THead
|
|
90
|
+
: never;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
Extracts the type of an array or tuple minus the first element.
|
|
94
|
+
*/
|
|
95
|
+
export type ArrayTail<TArray extends UnknownArrayOrTuple> = TArray extends readonly [unknown, ...infer TTail] ? TTail : [];
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
Returns a boolean for whether the string is lowercased.
|
|
99
|
+
*/
|
|
100
|
+
export type IsLowerCase<T extends string> = T extends Lowercase<T> ? true : false;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
Returns a boolean for whether the string is uppercased.
|
|
104
|
+
*/
|
|
105
|
+
export type IsUpperCase<T extends string> = T extends Uppercase<T> ? true : false;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
Returns a boolean for whether the string is numeric.
|
|
109
|
+
*/
|
|
110
|
+
export type IsNumeric<T extends string> = T extends `${number}` ? true : false;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type {JsonPrimitive, JsonValue} from './basic';
|
|
2
|
+
|
|
3
|
+
type JsonifiableObject = {[Key in string]?: Jsonifiable} | {toJSON: () => Jsonifiable};
|
|
4
|
+
type JsonifiableArray = readonly Jsonifiable[];
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
Matches a value that can be losslessly converted to JSON.
|
|
8
|
+
|
|
9
|
+
Can be used to type values that you expect to pass to `JSON.stringify`.
|
|
10
|
+
|
|
11
|
+
`undefined` is allowed in object fields (for example, `{a?: number}`) as a special case even though `JSON.stringify({a: undefined})` is `{}` because it makes this class more widely useful and checking for undefined-but-present values is likely an anti-pattern.
|
|
12
|
+
|
|
13
|
+
@example
|
|
14
|
+
```
|
|
15
|
+
import type {Jsonifiable} from 'type-fest';
|
|
16
|
+
|
|
17
|
+
// @ts-expect-error
|
|
18
|
+
const error: Jsonifiable = {
|
|
19
|
+
map: new Map([['a', 1]]),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
JSON.stringify(error);
|
|
23
|
+
//=> {"map": {}}
|
|
24
|
+
|
|
25
|
+
const good: Jsonifiable = {
|
|
26
|
+
number: 3,
|
|
27
|
+
date: new Date(),
|
|
28
|
+
missing: undefined,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
JSON.stringify(good);
|
|
32
|
+
//=> {"number": 3, "date": "2022-10-17T22:22:35.920Z"}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
@category JSON
|
|
36
|
+
*/
|
|
37
|
+
export type Jsonifiable = JsonPrimitive | JsonifiableObject | JsonifiableArray;
|
package/source/jsonify.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {JsonPrimitive, JsonValue} from './basic';
|
|
2
|
+
import type {EmptyObject} from './empty-object';
|
|
2
3
|
import type {Merge} from './merge';
|
|
3
4
|
import type {NegativeInfinity, PositiveInfinity} from './numeric';
|
|
4
5
|
import type {TypedArray} from './typed-array';
|
|
@@ -95,12 +96,12 @@ export type Jsonify<T> =
|
|
|
95
96
|
// Any object with toJSON is special case
|
|
96
97
|
? T extends {toJSON(): infer J} ? (() => J) extends (() => JsonValue) // Is J assignable to JsonValue?
|
|
97
98
|
? J // Then T is Jsonable and its Jsonable value is J
|
|
98
|
-
:
|
|
99
|
+
: Jsonify<J> // Maybe if we look a level deeper we'll find a JsonValue
|
|
99
100
|
// Instanced primitives are objects
|
|
100
101
|
: T extends Number ? number
|
|
101
102
|
: T extends String ? string
|
|
102
103
|
: T extends Boolean ? boolean
|
|
103
|
-
: T extends Map<any, any> | Set<any> ?
|
|
104
|
+
: T extends Map<any, any> | Set<any> ? EmptyObject
|
|
104
105
|
: T extends TypedArray ? Record<string, number>
|
|
105
106
|
: T extends any[]
|
|
106
107
|
? {[I in keyof T]: T[I] extends NotJsonable ? null : Jsonify<T[I]>}
|
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
import type {ConditionalSimplifyDeep} from './conditional-simplify';
|
|
2
|
+
import type {OmitIndexSignature} from './omit-index-signature';
|
|
3
|
+
import type {PickIndexSignature} from './pick-index-signature';
|
|
4
|
+
import type {EnforceOptional} from './enforce-optional';
|
|
5
|
+
import type {Merge} from './merge';
|
|
6
|
+
import type {
|
|
7
|
+
ArrayTail,
|
|
8
|
+
FirstArrayElement,
|
|
9
|
+
IsBothExtends,
|
|
10
|
+
NonEmptyTuple,
|
|
11
|
+
UnknownArrayOrTuple,
|
|
12
|
+
UnknownRecord,
|
|
13
|
+
} from './internal';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
Deeply simplifies an object excluding iterables and functions. Used internally to improve the UX and accept both interfaces and type aliases as inputs.
|
|
17
|
+
*/
|
|
18
|
+
type SimplifyDeep<Type> = ConditionalSimplifyDeep<Type, Function | Iterable<unknown>, object>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
Try to merge two record properties or return the source property value, preserving `undefined` properties values in both cases.
|
|
22
|
+
*/
|
|
23
|
+
type MergeDeepRecordProperty<
|
|
24
|
+
Destination,
|
|
25
|
+
Source,
|
|
26
|
+
Options extends MergeDeepInternalOptions,
|
|
27
|
+
> = undefined extends Source
|
|
28
|
+
? MergeDeepOrReturn<Source, Exclude<Destination, undefined>, Exclude<Source, undefined>, Options> | undefined
|
|
29
|
+
: MergeDeepOrReturn<Source, Destination, Source, Options>;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
Walk through the union of the keys of the two objects and test in which object the properties are defined.
|
|
33
|
+
- If the source does not contain the key, the value of the destination is returned.
|
|
34
|
+
- If the source contains the key and the destination does not contain the key, the value of the source is returned.
|
|
35
|
+
- If both contain the key, try to merge according to the chosen {@link MergeDeepOptions options} or return the source if unable to merge.
|
|
36
|
+
*/
|
|
37
|
+
type DoMergeDeepRecord<
|
|
38
|
+
Destination extends UnknownRecord,
|
|
39
|
+
Source extends UnknownRecord,
|
|
40
|
+
Options extends MergeDeepInternalOptions,
|
|
41
|
+
> = EnforceOptional<{
|
|
42
|
+
[Key in keyof Destination | keyof Source]: Key extends keyof Source
|
|
43
|
+
? Key extends keyof Destination
|
|
44
|
+
? MergeDeepRecordProperty<Destination[Key], Source[Key], Options>
|
|
45
|
+
: Source[Key]
|
|
46
|
+
: Key extends keyof Destination
|
|
47
|
+
? Destination[Key]
|
|
48
|
+
: never;
|
|
49
|
+
}>;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
Wrapper around {@link DoMergeDeepRecord} which preserves index signatures.
|
|
53
|
+
*/
|
|
54
|
+
type MergeDeepRecord<
|
|
55
|
+
Destination extends UnknownRecord,
|
|
56
|
+
Source extends UnknownRecord,
|
|
57
|
+
Options extends MergeDeepInternalOptions,
|
|
58
|
+
> = DoMergeDeepRecord<OmitIndexSignature<Destination>, OmitIndexSignature<Source>, Options>
|
|
59
|
+
& Merge<PickIndexSignature<Destination>, PickIndexSignature<Source>>;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
Pick the rest type.
|
|
63
|
+
|
|
64
|
+
@example
|
|
65
|
+
```
|
|
66
|
+
type Rest1 = PickRestType<[]>; // => []
|
|
67
|
+
type Rest2 = PickRestType<[string]>; // => []
|
|
68
|
+
type Rest3 = PickRestType<[...number[]]>; // => number[]
|
|
69
|
+
type Rest4 = PickRestType<[string, ...number[]]>; // => number[]
|
|
70
|
+
type Rest5 = PickRestType<string[]>; // => string[]
|
|
71
|
+
```
|
|
72
|
+
*/
|
|
73
|
+
type PickRestType<Type extends UnknownArrayOrTuple> = number extends Type['length']
|
|
74
|
+
? ArrayTail<Type> extends [] ? Type : PickRestType<ArrayTail<Type>>
|
|
75
|
+
: [];
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
Omit the rest type.
|
|
79
|
+
|
|
80
|
+
@example
|
|
81
|
+
```
|
|
82
|
+
type Tuple1 = OmitRestType<[]>; // => []
|
|
83
|
+
type Tuple2 = OmitRestType<[string]>; // => [string]
|
|
84
|
+
type Tuple3 = OmitRestType<[...number[]]>; // => []
|
|
85
|
+
type Tuple4 = OmitRestType<[string, ...number[]]>; // => [string]
|
|
86
|
+
type Tuple5 = OmitRestType<[string, boolean[], ...number[]]>; // => [string, boolean[]]
|
|
87
|
+
type Tuple6 = OmitRestType<string[]>; // => []
|
|
88
|
+
```
|
|
89
|
+
*/
|
|
90
|
+
type OmitRestType<Type extends UnknownArrayOrTuple, Result extends UnknownArrayOrTuple = []> = number extends Type['length']
|
|
91
|
+
? ArrayTail<Type> extends [] ? Result : OmitRestType<ArrayTail<Type>, [...Result, FirstArrayElement<Type>]>
|
|
92
|
+
: Type;
|
|
93
|
+
|
|
94
|
+
// Utility to avoid picking two times the type.
|
|
95
|
+
type TypeNumberOrType<Type extends UnknownArrayOrTuple> = Type[number] extends never ? Type : Type[number];
|
|
96
|
+
|
|
97
|
+
// Pick the rest type (array) and try to get the intrinsic type or return the provided type.
|
|
98
|
+
type PickRestTypeFlat<Type extends UnknownArrayOrTuple> = TypeNumberOrType<PickRestType<Type>>;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
Try to merge two array/tuple elements or return the source element if the end of the destination is reached or vis-versa.
|
|
102
|
+
*/
|
|
103
|
+
type MergeDeepArrayOrTupleElements<
|
|
104
|
+
Destination,
|
|
105
|
+
Source,
|
|
106
|
+
Options extends MergeDeepInternalOptions,
|
|
107
|
+
> = Source extends []
|
|
108
|
+
? Destination
|
|
109
|
+
: Destination extends []
|
|
110
|
+
? Source
|
|
111
|
+
: MergeDeepOrReturn<Source, Destination, Source, Options>;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
Merge two tuples recursively.
|
|
115
|
+
*/
|
|
116
|
+
type DoMergeDeepTupleAndTupleRecursive<
|
|
117
|
+
Destination extends UnknownArrayOrTuple,
|
|
118
|
+
Source extends UnknownArrayOrTuple,
|
|
119
|
+
DestinationRestType,
|
|
120
|
+
SourceRestType,
|
|
121
|
+
Options extends MergeDeepInternalOptions,
|
|
122
|
+
> = Destination extends []
|
|
123
|
+
? Source extends []
|
|
124
|
+
? []
|
|
125
|
+
: MergeArrayTypeAndTuple<DestinationRestType, Source, Options>
|
|
126
|
+
: Source extends []
|
|
127
|
+
? MergeTupleAndArrayType<Destination, SourceRestType, Options>
|
|
128
|
+
: [
|
|
129
|
+
MergeDeepArrayOrTupleElements<FirstArrayElement<Destination>, FirstArrayElement<Source>, Options>,
|
|
130
|
+
...DoMergeDeepTupleAndTupleRecursive<ArrayTail<Destination>, ArrayTail<Source>, DestinationRestType, SourceRestType, Options>,
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
Merge two tuples recursively taking into account a possible rest element.
|
|
135
|
+
*/
|
|
136
|
+
type MergeDeepTupleAndTupleRecursive<
|
|
137
|
+
Destination extends UnknownArrayOrTuple,
|
|
138
|
+
Source extends UnknownArrayOrTuple,
|
|
139
|
+
Options extends MergeDeepInternalOptions,
|
|
140
|
+
> = [
|
|
141
|
+
...DoMergeDeepTupleAndTupleRecursive<OmitRestType<Destination>, OmitRestType<Source>, PickRestTypeFlat<Destination>, PickRestTypeFlat<Source>, Options>,
|
|
142
|
+
...MergeDeepArrayOrTupleElements<PickRestType<Destination>, PickRestType<Source>, Options>,
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
Merge an array type with a tuple recursively.
|
|
147
|
+
*/
|
|
148
|
+
type MergeTupleAndArrayType<
|
|
149
|
+
Tuple extends UnknownArrayOrTuple,
|
|
150
|
+
ArrayType,
|
|
151
|
+
Options extends MergeDeepInternalOptions,
|
|
152
|
+
> = Tuple extends []
|
|
153
|
+
? Tuple
|
|
154
|
+
: [
|
|
155
|
+
MergeDeepArrayOrTupleElements<FirstArrayElement<Tuple>, ArrayType, Options>,
|
|
156
|
+
...MergeTupleAndArrayType<ArrayTail<Tuple>, ArrayType, Options>,
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
Merge an array into a tuple recursively taking into account a possible rest element.
|
|
161
|
+
*/
|
|
162
|
+
type MergeDeepTupleAndArrayRecursive<
|
|
163
|
+
Destination extends UnknownArrayOrTuple,
|
|
164
|
+
Source extends UnknownArrayOrTuple,
|
|
165
|
+
Options extends MergeDeepInternalOptions,
|
|
166
|
+
> = [
|
|
167
|
+
...MergeTupleAndArrayType<OmitRestType<Destination>, Source[number], Options>,
|
|
168
|
+
...MergeDeepArrayOrTupleElements<PickRestType<Destination>, PickRestType<Source>, Options>,
|
|
169
|
+
];
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
Merge a tuple with an array type recursively.
|
|
173
|
+
*/
|
|
174
|
+
type MergeArrayTypeAndTuple<
|
|
175
|
+
ArrayType,
|
|
176
|
+
Tuple extends UnknownArrayOrTuple,
|
|
177
|
+
Options extends MergeDeepInternalOptions,
|
|
178
|
+
> = Tuple extends []
|
|
179
|
+
? Tuple
|
|
180
|
+
: [
|
|
181
|
+
MergeDeepArrayOrTupleElements<ArrayType, FirstArrayElement<Tuple>, Options>,
|
|
182
|
+
...MergeArrayTypeAndTuple<ArrayType, ArrayTail<Tuple>, Options>,
|
|
183
|
+
];
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
Merge a tuple into an array recursively taking into account a possible rest element.
|
|
187
|
+
*/
|
|
188
|
+
type MergeDeepArrayAndTupleRecursive<
|
|
189
|
+
Destination extends UnknownArrayOrTuple,
|
|
190
|
+
Source extends UnknownArrayOrTuple,
|
|
191
|
+
Options extends MergeDeepInternalOptions,
|
|
192
|
+
> = [
|
|
193
|
+
...MergeArrayTypeAndTuple<Destination[number], OmitRestType<Source>, Options>,
|
|
194
|
+
...MergeDeepArrayOrTupleElements<PickRestType<Destination>, PickRestType<Source>, Options>,
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
Merge mode for array/tuple elements.
|
|
199
|
+
*/
|
|
200
|
+
type ArrayMergeMode = 'spread' | 'replace';
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
Test if it should spread top-level arrays.
|
|
204
|
+
*/
|
|
205
|
+
type ShouldSpread<Options extends MergeDeepInternalOptions> = Options['spreadTopLevelArrays'] extends false
|
|
206
|
+
? Options['arrayMergeMode'] extends 'spread' ? true : false
|
|
207
|
+
: true;
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
Merge two arrays/tuples according to the chosen {@link MergeDeepOptions.arrayMergeMode arrayMergeMode} option.
|
|
211
|
+
*/
|
|
212
|
+
type DoMergeArrayOrTuple<
|
|
213
|
+
Destination extends UnknownArrayOrTuple,
|
|
214
|
+
Source extends UnknownArrayOrTuple,
|
|
215
|
+
Options extends MergeDeepInternalOptions,
|
|
216
|
+
> = ShouldSpread<Options> extends true
|
|
217
|
+
? Array<Exclude<Destination, undefined>[number] | Exclude<Source, undefined>[number]>
|
|
218
|
+
: Source; // 'replace'
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
Merge two arrays recursively.
|
|
222
|
+
|
|
223
|
+
If the two arrays are multi-level, we merge deeply, otherwise we merge the first level only.
|
|
224
|
+
|
|
225
|
+
Note: The `[number]` accessor is used to test the type of the second level.
|
|
226
|
+
*/
|
|
227
|
+
type MergeDeepArrayRecursive<
|
|
228
|
+
Destination extends UnknownArrayOrTuple,
|
|
229
|
+
Source extends UnknownArrayOrTuple,
|
|
230
|
+
Options extends MergeDeepInternalOptions,
|
|
231
|
+
> = Destination[number] extends UnknownArrayOrTuple
|
|
232
|
+
? Source[number] extends UnknownArrayOrTuple
|
|
233
|
+
? Array<MergeDeepArrayOrTupleRecursive<Destination[number], Source[number], Options>>
|
|
234
|
+
: DoMergeArrayOrTuple<Destination, Source, Options>
|
|
235
|
+
: Destination[number] extends UnknownRecord
|
|
236
|
+
? Source[number] extends UnknownRecord
|
|
237
|
+
? Array<SimplifyDeep<MergeDeepRecord<Destination[number], Source[number], Options>>>
|
|
238
|
+
: DoMergeArrayOrTuple<Destination, Source, Options>
|
|
239
|
+
: DoMergeArrayOrTuple<Destination, Source, Options>;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
Merge two array/tuple recursively by selecting one of the four strategies according to the type of inputs.
|
|
243
|
+
|
|
244
|
+
- tuple/tuple
|
|
245
|
+
- tuple/array
|
|
246
|
+
- array/tuple
|
|
247
|
+
- array/array
|
|
248
|
+
*/
|
|
249
|
+
type MergeDeepArrayOrTupleRecursive<
|
|
250
|
+
Destination extends UnknownArrayOrTuple,
|
|
251
|
+
Source extends UnknownArrayOrTuple,
|
|
252
|
+
Options extends MergeDeepInternalOptions,
|
|
253
|
+
> = IsBothExtends<NonEmptyTuple, Destination, Source> extends true
|
|
254
|
+
? MergeDeepTupleAndTupleRecursive<Destination, Source, Options>
|
|
255
|
+
: Destination extends NonEmptyTuple
|
|
256
|
+
? MergeDeepTupleAndArrayRecursive<Destination, Source, Options>
|
|
257
|
+
: Source extends NonEmptyTuple
|
|
258
|
+
? MergeDeepArrayAndTupleRecursive<Destination, Source, Options>
|
|
259
|
+
: MergeDeepArrayRecursive<Destination, Source, Options>;
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
Merge two array/tuple according to {@link MergeDeepOptions.recurseIntoArrays recurseIntoArrays} option.
|
|
263
|
+
*/
|
|
264
|
+
type MergeDeepArrayOrTuple<
|
|
265
|
+
Destination extends UnknownArrayOrTuple,
|
|
266
|
+
Source extends UnknownArrayOrTuple,
|
|
267
|
+
Options extends MergeDeepInternalOptions,
|
|
268
|
+
> = Options['recurseIntoArrays'] extends true
|
|
269
|
+
? MergeDeepArrayOrTupleRecursive<Destination, Source, Options>
|
|
270
|
+
: DoMergeArrayOrTuple<Destination, Source, Options>;
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
Try to merge two objects or two arrays/tuples recursively into a new type or return the default value.
|
|
274
|
+
*/
|
|
275
|
+
type MergeDeepOrReturn<
|
|
276
|
+
DefaultType,
|
|
277
|
+
Destination,
|
|
278
|
+
Source,
|
|
279
|
+
Options extends MergeDeepInternalOptions,
|
|
280
|
+
> = SimplifyDeep<[undefined] extends [Destination | Source]
|
|
281
|
+
? DefaultType
|
|
282
|
+
: Destination extends UnknownRecord
|
|
283
|
+
? Source extends UnknownRecord
|
|
284
|
+
? MergeDeepRecord<Destination, Source, Options>
|
|
285
|
+
: DefaultType
|
|
286
|
+
: Destination extends UnknownArrayOrTuple
|
|
287
|
+
? Source extends UnknownArrayOrTuple
|
|
288
|
+
? MergeDeepArrayOrTuple<Destination, Source, Merge<Options, {spreadTopLevelArrays: false}>>
|
|
289
|
+
: DefaultType
|
|
290
|
+
: DefaultType>;
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
MergeDeep options.
|
|
294
|
+
|
|
295
|
+
@see {@link MergeDeep}
|
|
296
|
+
*/
|
|
297
|
+
export type MergeDeepOptions = {
|
|
298
|
+
/**
|
|
299
|
+
Merge mode for array and tuple.
|
|
300
|
+
|
|
301
|
+
When we walk through the properties of the objects and the same key is found and both are array or tuple, a merge mode must be chosen:
|
|
302
|
+
- `replace`: Replaces the destination value by the source value. This is the default mode.
|
|
303
|
+
- `spread`: Spreads the destination and the source values.
|
|
304
|
+
|
|
305
|
+
See {@link MergeDeep} for usages and examples.
|
|
306
|
+
|
|
307
|
+
Note: Top-level arrays and tuples are always spread.
|
|
308
|
+
|
|
309
|
+
@default 'spread'
|
|
310
|
+
*/
|
|
311
|
+
arrayMergeMode?: ArrayMergeMode;
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
Whether to affect the individual elements of arrays and tuples.
|
|
315
|
+
|
|
316
|
+
If this option is set to `true` the following rules are applied:
|
|
317
|
+
- If the source does not contain the key, the value of the destination is returned.
|
|
318
|
+
- If the source contains the key and the destination does not contain the key, the value of the source is returned.
|
|
319
|
+
- If both contain the key, try to merge according to the chosen {@link MergeDeepOptions.arrayMergeMode arrayMergeMode} or return the source if unable to merge.
|
|
320
|
+
|
|
321
|
+
@default false
|
|
322
|
+
*/
|
|
323
|
+
recurseIntoArrays?: boolean;
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
Internal options.
|
|
328
|
+
*/
|
|
329
|
+
type MergeDeepInternalOptions = Merge<MergeDeepOptions, {spreadTopLevelArrays?: boolean}>;
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
Merge default and internal options with user provided options.
|
|
333
|
+
*/
|
|
334
|
+
type DefaultMergeDeepOptions<Options extends MergeDeepOptions> = Merge<{
|
|
335
|
+
arrayMergeMode: 'replace';
|
|
336
|
+
recurseIntoArrays: false;
|
|
337
|
+
spreadTopLevelArrays: true;
|
|
338
|
+
}, Options>;
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
This utility selects the correct entry point with the corresponding default options. This avoids re-merging the options at each iteration.
|
|
342
|
+
*/
|
|
343
|
+
type MergeDeepWithDefaultOptions<Destination, Source, Options extends MergeDeepOptions> = SimplifyDeep<
|
|
344
|
+
[undefined] extends [Destination | Source]
|
|
345
|
+
? never
|
|
346
|
+
: Destination extends UnknownRecord
|
|
347
|
+
? Source extends UnknownRecord
|
|
348
|
+
? MergeDeepRecord<Destination, Source, DefaultMergeDeepOptions<Options>>
|
|
349
|
+
: never
|
|
350
|
+
: Destination extends UnknownArrayOrTuple
|
|
351
|
+
? Source extends UnknownArrayOrTuple
|
|
352
|
+
? MergeDeepArrayOrTuple<Destination, Source, DefaultMergeDeepOptions<Options>>
|
|
353
|
+
: never
|
|
354
|
+
: never
|
|
355
|
+
>;
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
Merge two objects or two arrays/tuples recursively into a new type.
|
|
359
|
+
|
|
360
|
+
- Properties that only exist in one object are copied into the new object.
|
|
361
|
+
- Properties that exist in both objects are merged if possible or replaced by the one of the source if not.
|
|
362
|
+
- Top-level arrays and tuples are always spread.
|
|
363
|
+
- By default, inner arrays and tuples are replaced. See {@link MergeDeepOptions.arrayMergeMode arrayMergeMode} option to change this behaviour.
|
|
364
|
+
- By default, individual array/tuple elements are not affected. See {@link MergeDeepOptions.recurseIntoArrays recurseIntoArrays} option to change this behaviour.
|
|
365
|
+
|
|
366
|
+
@example
|
|
367
|
+
```
|
|
368
|
+
import type {MergeDeep} from 'type-fest';
|
|
369
|
+
|
|
370
|
+
type Foo = {
|
|
371
|
+
life: number;
|
|
372
|
+
items: string[];
|
|
373
|
+
a: {b: string; c: boolean; d: number[]};
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
interface Bar {
|
|
377
|
+
name: string;
|
|
378
|
+
items: number[];
|
|
379
|
+
a: {b: number; d: boolean[]};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
type FooBar = MergeDeep<Foo, Bar>;
|
|
383
|
+
// {
|
|
384
|
+
// life: number;
|
|
385
|
+
// name: string;
|
|
386
|
+
// items: number[];
|
|
387
|
+
// a: {b: number; c: boolean; d: boolean[]};
|
|
388
|
+
// }
|
|
389
|
+
|
|
390
|
+
type FooBar = MergeDeep<Foo, Bar, {arrayMergeMode: 'spread'}>;
|
|
391
|
+
// {
|
|
392
|
+
// life: number;
|
|
393
|
+
// name: string;
|
|
394
|
+
// items: (string | number)[];
|
|
395
|
+
// a: {b: number; c: boolean; d: (number | boolean)[]};
|
|
396
|
+
// }
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
@example
|
|
400
|
+
```
|
|
401
|
+
import type {MergeDeep} from 'type-fest';
|
|
402
|
+
|
|
403
|
+
// Merge two arrays
|
|
404
|
+
type ArrayMerge = MergeDeep<string[], number[]>; // => (string | number)[]
|
|
405
|
+
|
|
406
|
+
// Merge two tuples
|
|
407
|
+
type TupleMerge = MergeDeep<[1, 2, 3], ['a', 'b']>; // => (1 | 2 | 3 | 'a' | 'b')[]
|
|
408
|
+
|
|
409
|
+
// Merge an array into a tuple
|
|
410
|
+
type TupleArrayMerge = MergeDeep<[1, 2, 3], string[]>; // => (string | 1 | 2 | 3)[]
|
|
411
|
+
|
|
412
|
+
// Merge a tuple into an array
|
|
413
|
+
type ArrayTupleMerge = MergeDeep<number[], ['a', 'b']>; // => (number | 'b' | 'a')[]
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
@example
|
|
417
|
+
```
|
|
418
|
+
import type {MergeDeep, MergeDeepOptions} from 'type-fest';
|
|
419
|
+
|
|
420
|
+
type Foo = {foo: 'foo'; fooBar: string[]};
|
|
421
|
+
type Bar = {bar: 'bar'; fooBar: number[]};
|
|
422
|
+
|
|
423
|
+
type FooBar = MergeDeep<Foo, Bar>;
|
|
424
|
+
// { foo: "foo"; bar: "bar"; fooBar: number[]}
|
|
425
|
+
|
|
426
|
+
type FooBarSpread = MergeDeep<Foo, Bar, {arrayMergeMode: 'spread'}>;
|
|
427
|
+
// { foo: "foo"; bar: "bar"; fooBar: (string | number)[]}
|
|
428
|
+
|
|
429
|
+
type FooBarArray = MergeDeep<Foo[], Bar[]>;
|
|
430
|
+
// (Foo | Bar)[]
|
|
431
|
+
|
|
432
|
+
type FooBarArrayDeep = MergeDeep<Foo[], Bar[], {recurseIntoArrays: true}>;
|
|
433
|
+
// FooBar[]
|
|
434
|
+
|
|
435
|
+
type FooBarArraySpreadDeep = MergeDeep<Foo[], Bar[], {recurseIntoArrays: true; arrayMergeMode: 'spread'}>;
|
|
436
|
+
// FooBarSpread[]
|
|
437
|
+
|
|
438
|
+
type FooBarTupleDeep = MergeDeep<[Foo, true, 42], [Bar, 'life'], {recurseIntoArrays: true}>;
|
|
439
|
+
// [FooBar, 'life', 42]
|
|
440
|
+
|
|
441
|
+
type FooBarTupleWithArrayDeep = MergeDeep<[Foo[], true], [Bar[], 'life', 42], {recurseIntoArrays: true}>;
|
|
442
|
+
// [FooBar[], 'life', 42]
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
@example
|
|
446
|
+
```
|
|
447
|
+
import type {MergeDeep, MergeDeepOptions} from 'type-fest';
|
|
448
|
+
|
|
449
|
+
function mergeDeep<Destination, Source, Options extends MergeDeepOptions = {}>(
|
|
450
|
+
destination: Destination,
|
|
451
|
+
source: Source,
|
|
452
|
+
options?: Options,
|
|
453
|
+
): MergeDeep<Destination, Source, Options> {
|
|
454
|
+
// Make your implementation ...
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
@experimental This type is marked as experimental because it depends on {@link ConditionalSimplifyDeep} which itself is experimental.
|
|
459
|
+
|
|
460
|
+
@see {@link MergeDeepOptions}
|
|
461
|
+
|
|
462
|
+
@category Array
|
|
463
|
+
@category Object
|
|
464
|
+
@category Utilities
|
|
465
|
+
*/
|
|
466
|
+
export type MergeDeep<Destination, Source, Options extends MergeDeepOptions = {}> = MergeDeepWithDefaultOptions<
|
|
467
|
+
SimplifyDeep<Destination>,
|
|
468
|
+
SimplifyDeep<Source>,
|
|
469
|
+
Options
|
|
470
|
+
>;
|
|
@@ -8,7 +8,7 @@ declare global {
|
|
|
8
8
|
/**
|
|
9
9
|
@remarks
|
|
10
10
|
The TC39 observable proposal defines a `closed` property, but some implementations (such as xstream) do not as of 10/08/2021.
|
|
11
|
-
As well, some
|
|
11
|
+
As well, some guidance on making an `Observable` to not include `closed` property.
|
|
12
12
|
@see https://github.com/tc39/proposal-observable/blob/master/src/Observable.js#L129-L130
|
|
13
13
|
@see https://github.com/staltz/xstream/blob/6c22580c1d84d69773ee4b0905df44ad464955b3/src/index.ts#L79-L85
|
|
14
14
|
@see https://github.com/benlesh/symbol-observable#making-an-object-observable
|
package/source/package-json.d.ts
CHANGED
|
@@ -240,7 +240,7 @@ declare namespace PackageJson {
|
|
|
240
240
|
Import map entries of a module, optionally with conditions.
|
|
241
241
|
*/
|
|
242
242
|
export type Imports = { // eslint-disable-line @typescript-eslint/consistent-indexed-object-style
|
|
243
|
-
[key: string]: string | {[key in ExportCondition]: Exports};
|
|
243
|
+
[key: `#${string}`]: string | {[key in ExportCondition]: Exports};
|
|
244
244
|
};
|
|
245
245
|
|
|
246
246
|
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
package/source/schema.d.ts
CHANGED
|
@@ -2,23 +2,32 @@ import type {Except} from './except';
|
|
|
2
2
|
import type {Simplify} from './simplify';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
Create a type that makes the given keys non-nullable
|
|
5
|
+
Create a type that makes the given keys non-nullable, where the remaining keys are kept as is.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
If no keys are given, all keys will be made non-nullable.
|
|
8
|
+
|
|
9
|
+
Use-case: You want to define a single model where the only thing that changes is whether or not some or all of the keys are non-nullable.
|
|
8
10
|
|
|
9
11
|
@example
|
|
10
12
|
```
|
|
11
13
|
import type {SetNonNullable} from 'type-fest';
|
|
12
14
|
|
|
13
15
|
type Foo = {
|
|
14
|
-
a: number;
|
|
16
|
+
a: number | null;
|
|
15
17
|
b: string | undefined;
|
|
16
18
|
c?: boolean | null;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
type SomeNonNullable = SetNonNullable<Foo, 'b' | 'c'>;
|
|
20
22
|
// type SomeNonNullable = {
|
|
21
|
-
// a: number;
|
|
23
|
+
// a: number | null;
|
|
24
|
+
// b: string; // Can no longer be undefined.
|
|
25
|
+
// c?: boolean; // Can no longer be null, but is still optional.
|
|
26
|
+
// }
|
|
27
|
+
|
|
28
|
+
type AllNonNullable = SetNonNullable<Foo>;
|
|
29
|
+
// type AllNonNullable = {
|
|
30
|
+
// a: number; // Can no longer be null.
|
|
22
31
|
// b: string; // Can no longer be undefined.
|
|
23
32
|
// c?: boolean; // Can no longer be null, but is still optional.
|
|
24
33
|
// }
|
|
@@ -26,7 +35,7 @@ type SomeNonNullable = SetNonNullable<Foo, 'b' | 'c'>;
|
|
|
26
35
|
|
|
27
36
|
@category Object
|
|
28
37
|
*/
|
|
29
|
-
export type SetNonNullable<BaseType, Keys extends keyof BaseType> =
|
|
38
|
+
export type SetNonNullable<BaseType, Keys extends keyof BaseType = keyof BaseType> =
|
|
30
39
|
Simplify<
|
|
31
40
|
// Pick just the keys that are readonly from the base type.
|
|
32
41
|
Except<BaseType, Keys> &
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type {IsLowerCase, IsNumeric, IsUpperCase, WordSeparators} from './internal';
|
|
2
|
+
|
|
3
|
+
type SkipEmptyWord<Word extends string> = Word extends '' ? [] : [Word];
|
|
4
|
+
|
|
5
|
+
type RemoveLastCharacter<Sentence extends string, Character extends string> = Sentence extends `${infer LeftSide}${Character}`
|
|
6
|
+
? SkipEmptyWord<LeftSide>
|
|
7
|
+
: never;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
Split a string (almost) like Lodash's `_.words()` function.
|
|
11
|
+
|
|
12
|
+
- Split on each word that begins with a capital letter.
|
|
13
|
+
- Split on each {@link WordSeparators}.
|
|
14
|
+
- Split on numeric sequence.
|
|
15
|
+
|
|
16
|
+
@example
|
|
17
|
+
```
|
|
18
|
+
type Words0 = SplitWords<'helloWorld'>; // ['hello', 'World']
|
|
19
|
+
type Words1 = SplitWords<'helloWORLD'>; // ['hello', 'WORLD']
|
|
20
|
+
type Words2 = SplitWords<'hello-world'>; // ['hello', 'world']
|
|
21
|
+
type Words3 = SplitWords<'--hello the_world'>; // ['hello', 'the', 'world']
|
|
22
|
+
type Words4 = SplitWords<'lifeIs42'>; // ['life', 'Is', '42']
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
@internal
|
|
26
|
+
@category Change case
|
|
27
|
+
@category Template literal
|
|
28
|
+
*/
|
|
29
|
+
export type SplitWords<
|
|
30
|
+
Sentence extends string,
|
|
31
|
+
LastCharacter extends string = '',
|
|
32
|
+
CurrentWord extends string = '',
|
|
33
|
+
> = Sentence extends `${infer FirstCharacter}${infer RemainingCharacters}`
|
|
34
|
+
? FirstCharacter extends WordSeparators
|
|
35
|
+
// Skip word separator
|
|
36
|
+
? [...SkipEmptyWord<CurrentWord>, ...SplitWords<RemainingCharacters, LastCharacter>]
|
|
37
|
+
: LastCharacter extends ''
|
|
38
|
+
// Fist char of word
|
|
39
|
+
? SplitWords<RemainingCharacters, FirstCharacter, FirstCharacter>
|
|
40
|
+
// Case change: non-numeric to numeric, push word
|
|
41
|
+
: [false, true] extends [IsNumeric<LastCharacter>, IsNumeric<FirstCharacter>]
|
|
42
|
+
? [...SkipEmptyWord<CurrentWord>, ...SplitWords<RemainingCharacters, FirstCharacter, FirstCharacter>]
|
|
43
|
+
// Case change: numeric to non-numeric, push word
|
|
44
|
+
: [true, false] extends [IsNumeric<LastCharacter>, IsNumeric<FirstCharacter>]
|
|
45
|
+
? [...SkipEmptyWord<CurrentWord>, ...SplitWords<RemainingCharacters, FirstCharacter, FirstCharacter>]
|
|
46
|
+
// No case change: concat word
|
|
47
|
+
: [true, true] extends [IsNumeric<LastCharacter>, IsNumeric<FirstCharacter>]
|
|
48
|
+
? SplitWords<RemainingCharacters, FirstCharacter, `${CurrentWord}${FirstCharacter}`>
|
|
49
|
+
// Case change: lower to upper, push word
|
|
50
|
+
: [true, true] extends [IsLowerCase<LastCharacter>, IsUpperCase<FirstCharacter>]
|
|
51
|
+
? [...SkipEmptyWord<CurrentWord>, ...SplitWords<RemainingCharacters, FirstCharacter, FirstCharacter>]
|
|
52
|
+
// Case change: upper to lower, brings back the last character, push word
|
|
53
|
+
: [true, true] extends [IsUpperCase<LastCharacter>, IsLowerCase<FirstCharacter>]
|
|
54
|
+
? [...RemoveLastCharacter<CurrentWord, LastCharacter>, ...SplitWords<RemainingCharacters, FirstCharacter, `${LastCharacter}${FirstCharacter}`>]
|
|
55
|
+
// No case change: concat word
|
|
56
|
+
: SplitWords<RemainingCharacters, FirstCharacter, `${CurrentWord}${FirstCharacter}`>
|
|
57
|
+
: [...SkipEmptyWord<CurrentWord>];
|
package/source/stringified.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
Convert a tuple into a union type of its elements.
|
|
2
|
+
Convert a tuple/array into a union type of its elements.
|
|
3
3
|
|
|
4
4
|
This can be useful when you have a fixed set of allowed values and want a type defining only the allowed values, but do not want to repeat yourself.
|
|
5
5
|
|
|
@@ -48,4 +48,4 @@ type NumberBool = typeof numberBool[number];
|
|
|
48
48
|
|
|
49
49
|
@category Array
|
|
50
50
|
*/
|
|
51
|
-
export type TupleToUnion<ArrayType> = ArrayType extends readonly [
|
|
51
|
+
export type TupleToUnion<ArrayType> = ArrayType extends readonly unknown[] ? ArrayType[number] : never;
|