type-fest 5.3.1 → 5.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +3 -0
- package/package.json +4 -4
- package/readme.md +5 -0
- package/source/all-extend.d.ts +9 -6
- package/source/array-reverse.d.ts +84 -0
- package/source/array-slice.d.ts +1 -1
- package/source/array-splice.d.ts +3 -3
- package/source/conditional-pick-deep.d.ts +1 -1
- package/source/conditional-simplify-deep.d.ts +3 -3
- package/source/distributed-omit.d.ts +4 -4
- package/source/distributed-pick.d.ts +5 -5
- package/source/exact.d.ts +1 -1
- package/source/except.d.ts +5 -5
- package/source/exclusify-union.d.ts +41 -7
- package/source/fixed-length-array.d.ts +6 -6
- package/source/get.d.ts +9 -3
- package/source/int-closed-range.d.ts +2 -2
- package/source/int-range.d.ts +2 -2
- package/source/internal/object.d.ts +25 -1
- package/source/is-literal.d.ts +3 -2
- package/source/is-never.d.ts +2 -2
- package/source/is-unknown.d.ts +1 -1
- package/source/jsonifiable.d.ts +3 -5
- package/source/key-as-string.d.ts +1 -1
- package/source/merge-deep.d.ts +9 -9
- package/source/merge-exclusive.d.ts +3 -3
- package/source/merge.d.ts +4 -1
- package/source/non-empty-string.d.ts +3 -3
- package/source/non-empty-tuple.d.ts +2 -2
- package/source/numeric.d.ts +3 -3
- package/source/object-merge.d.ts +194 -0
- package/source/omit-deep.d.ts +6 -22
- package/source/omit-index-signature.d.ts +8 -4
- package/source/override-properties.d.ts +1 -1
- package/source/paths.d.ts +1 -1
- package/source/readonly-tuple.d.ts +2 -2
- package/source/required-deep.d.ts +2 -1
- package/source/schema.d.ts +10 -7
- package/source/set-non-nullable-deep.d.ts +1 -1
- package/source/set-parameter-type.d.ts +7 -7
- package/source/set-required-deep.d.ts +1 -7
- package/source/set-return-type.d.ts +1 -1
- package/source/split-on-rest-element.d.ts +2 -2
- package/source/subtract.d.ts +1 -1
- package/source/sum.d.ts +1 -1
- package/source/tuple-of.d.ts +1 -1
- package/source/tuple-to-object.d.ts +6 -6
- package/source/tuple-to-union.d.ts +1 -1
- package/source/union-to-intersection.d.ts +1 -1
- package/source/unknown-record.d.ts +6 -6
- package/source/unwrap-partial.d.ts +33 -0
- package/source/value-of.d.ts +2 -2
- package/source/words.d.ts +2 -2
package/source/merge-deep.d.ts
CHANGED
|
@@ -33,7 +33,7 @@ type MergeDeepRecordProperty<
|
|
|
33
33
|
Source,
|
|
34
34
|
Options extends MergeDeepInternalOptions,
|
|
35
35
|
> = undefined extends Source
|
|
36
|
-
? MergeDeepOrReturn<Source, Exclude<Destination, undefined>, Exclude<Source, undefined>, Options> | undefined
|
|
36
|
+
? MergeDeepOrReturn<Source, Exclude<Destination, undefined>, Exclude<Source, undefined>, Options> | (undefined extends Destination ? undefined : never)
|
|
37
37
|
: MergeDeepOrReturn<Source, Destination, Source, Options>;
|
|
38
38
|
|
|
39
39
|
/**
|
|
@@ -58,7 +58,7 @@ type DoMergeDeepRecord<
|
|
|
58
58
|
}
|
|
59
59
|
// Case in rule 3: Both the source and the destination contain the key.
|
|
60
60
|
& {
|
|
61
|
-
[Key in keyof Source as Key extends keyof Destination ? Key : never]: MergeDeepRecordProperty<Destination[Key], Source[Key], Options>;
|
|
61
|
+
[Key in keyof Source as Key extends keyof Destination ? Key : never]: MergeDeepRecordProperty<Required<Destination>[Key], Required<Source>[Key], Options>;
|
|
62
62
|
};
|
|
63
63
|
|
|
64
64
|
/**
|
|
@@ -446,25 +446,25 @@ type Foo = {foo: 'foo'; fooBar: string[]};
|
|
|
446
446
|
type Bar = {bar: 'bar'; fooBar: number[]};
|
|
447
447
|
|
|
448
448
|
type FooBar = MergeDeep<Foo, Bar>;
|
|
449
|
-
|
|
449
|
+
//=> {foo: 'foo'; bar: 'bar'; fooBar: number[]}
|
|
450
450
|
|
|
451
451
|
type FooBarSpread = MergeDeep<Foo, Bar, {arrayMergeMode: 'spread'}>;
|
|
452
|
-
|
|
452
|
+
//=> {foo: 'foo'; bar: 'bar'; fooBar: (string | number)[]}
|
|
453
453
|
|
|
454
454
|
type FooBarArray = MergeDeep<Foo[], Bar[]>;
|
|
455
|
-
|
|
455
|
+
//=> (Foo | Bar)[]
|
|
456
456
|
|
|
457
457
|
type FooBarArrayDeep = MergeDeep<Foo[], Bar[], {recurseIntoArrays: true}>;
|
|
458
|
-
|
|
458
|
+
//=> {foo: 'foo'; bar: 'bar'; fooBar: number[]}[]
|
|
459
459
|
|
|
460
460
|
type FooBarArraySpreadDeep = MergeDeep<Foo[], Bar[], {recurseIntoArrays: true; arrayMergeMode: 'spread'}>;
|
|
461
|
-
|
|
461
|
+
//=> {foo: 'foo'; bar: 'bar'; fooBar: (string | number)[]}[]
|
|
462
462
|
|
|
463
463
|
type FooBarTupleDeep = MergeDeep<[Foo, true, 42], [Bar, 'life'], {recurseIntoArrays: true}>;
|
|
464
|
-
|
|
464
|
+
//=> [{foo: 'foo'; bar: 'bar'; fooBar: number[]}, 'life', 42]
|
|
465
465
|
|
|
466
466
|
type FooBarTupleWithArrayDeep = MergeDeep<[Foo[], true], [Bar[], 'life', 42], {recurseIntoArrays: true}>;
|
|
467
|
-
|
|
467
|
+
//=> [{foo: 'foo'; bar: 'bar'; fooBar: number[]}[], 'life', 42]
|
|
468
468
|
```
|
|
469
469
|
|
|
470
470
|
@example
|
|
@@ -25,14 +25,14 @@ type ExclusiveOptions = MergeExclusive<ExclusiveVariation1, ExclusiveVariation2>
|
|
|
25
25
|
let exclusiveOptions: ExclusiveOptions;
|
|
26
26
|
|
|
27
27
|
exclusiveOptions = {exclusive1: true};
|
|
28
|
-
|
|
28
|
+
// Works
|
|
29
29
|
|
|
30
30
|
exclusiveOptions = {exclusive2: 'hi'};
|
|
31
|
-
|
|
31
|
+
// Works
|
|
32
32
|
|
|
33
33
|
// @ts-expect-error
|
|
34
34
|
exclusiveOptions = {exclusive1: true, exclusive2: 'hi'};
|
|
35
|
-
|
|
35
|
+
// Error
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
@category Object
|
package/source/merge.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ type Bar = {
|
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
export type FooBar = Merge<Foo, Bar>;
|
|
32
|
-
|
|
32
|
+
//=> {
|
|
33
33
|
// [x: string]: unknown;
|
|
34
34
|
// [x: number]: number;
|
|
35
35
|
// [x: symbol]: unknown;
|
|
@@ -39,6 +39,9 @@ export type FooBar = Merge<Foo, Bar>;
|
|
|
39
39
|
// }
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
+
Note: If you want a merge type that more accurately reflects the runtime behavior of object spread or `Object.assign`, refer to the {@link ObjectMerge} type.
|
|
43
|
+
|
|
44
|
+
@see {@link ObjectMerge}
|
|
42
45
|
@category Object
|
|
43
46
|
*/
|
|
44
47
|
export type Merge<Destination, Source> =
|
|
@@ -13,16 +13,16 @@ import type {NonEmptyString} from 'type-fest';
|
|
|
13
13
|
declare function foo<T extends string>(string: NonEmptyString<T>): void;
|
|
14
14
|
|
|
15
15
|
foo('a');
|
|
16
|
-
|
|
16
|
+
// OK
|
|
17
17
|
|
|
18
18
|
// @ts-expect-error
|
|
19
19
|
foo('');
|
|
20
|
-
|
|
20
|
+
// Error: Argument of type '""' is not assignable to parameter of type 'never'.
|
|
21
21
|
|
|
22
22
|
declare const someString: string;
|
|
23
23
|
// @ts-expect-error
|
|
24
24
|
foo(someString);
|
|
25
|
-
|
|
25
|
+
// Error: Argument of type 'string' is not assignable to parameter of type 'never'.
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
@category String
|
|
@@ -8,11 +8,11 @@ import type {NonEmptyTuple} from 'type-fest';
|
|
|
8
8
|
const sum = (...numbers: NonEmptyTuple<number>) => numbers.reduce((total, value) => total + value, 0);
|
|
9
9
|
|
|
10
10
|
sum(1, 2, 3);
|
|
11
|
-
|
|
11
|
+
// Ok
|
|
12
12
|
|
|
13
13
|
// @ts-expect-error
|
|
14
14
|
sum();
|
|
15
|
-
|
|
15
|
+
// Error: Expected at least 1 arguments, but got 0.
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
@see {@link RequireAtLeastOne} for objects
|
package/source/numeric.d.ts
CHANGED
|
@@ -74,13 +74,13 @@ type Float = Integer<1.5>;
|
|
|
74
74
|
// Supports non-decimal numbers
|
|
75
75
|
|
|
76
76
|
type OctalInteger = Integer<0o10>;
|
|
77
|
-
//=>
|
|
77
|
+
//=> 8
|
|
78
78
|
|
|
79
79
|
type BinaryInteger = Integer<0b10>;
|
|
80
|
-
//=>
|
|
80
|
+
//=> 2
|
|
81
81
|
|
|
82
82
|
type HexadecimalInteger = Integer<0x10>;
|
|
83
|
-
//=>
|
|
83
|
+
//=> 16
|
|
84
84
|
```
|
|
85
85
|
|
|
86
86
|
@example
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import type {If} from './if.d.ts';
|
|
2
|
+
import type {NormalizedKeys} from './internal/object.d.ts';
|
|
3
|
+
import type {IfNotAnyOrNever, IsExactOptionalPropertyTypesEnabled, MapsSetsOrArrays} from './internal/type.d.ts';
|
|
4
|
+
import type {IsNever} from './is-never.d.ts';
|
|
5
|
+
import type {IsOptionalKeyOf} from './is-optional-key-of.d.ts';
|
|
6
|
+
import type {OmitIndexSignature} from './omit-index-signature.d.ts';
|
|
7
|
+
import type {PickIndexSignature} from './pick-index-signature.d.ts';
|
|
8
|
+
import type {RequiredKeysOf} from './required-keys-of.d.ts';
|
|
9
|
+
import type {Simplify} from './simplify.d.ts';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
Merge two object types into a new object type, where keys from the second override keys from the first.
|
|
13
|
+
|
|
14
|
+
@example
|
|
15
|
+
```ts
|
|
16
|
+
import type {ObjectMerge} from 'type-fest';
|
|
17
|
+
|
|
18
|
+
type PartialOverride = ObjectMerge<{foo: string; bar: string}, {foo: number; baz: number}>;
|
|
19
|
+
//=> {foo: number; baz: number; bar: string}
|
|
20
|
+
|
|
21
|
+
type CompleteOverride = ObjectMerge<{foo: string; bar: number}, {foo: number; bar: string; baz: boolean}>;
|
|
22
|
+
//=> {foo: number; bar: string; baz: boolean}
|
|
23
|
+
|
|
24
|
+
type NoOverride = ObjectMerge<{foo: string; bar: number}, {baz: boolean; qux: bigint}>;
|
|
25
|
+
//=> {baz: boolean; qux: bigint; foo: string; bar: number}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Use-cases:
|
|
29
|
+
|
|
30
|
+
Can be used to accurately type object spread and `Object.assign`. The built-in inference for these operations can sometimes be unsound, especially when index signatures are involved.
|
|
31
|
+
|
|
32
|
+
In the following example, both object spread and `Object.assign` produce a type that allows unsafe usage, whereas `ObjectMerge` produces a type that prevents this unsafe access.
|
|
33
|
+
|
|
34
|
+
@example
|
|
35
|
+
```ts
|
|
36
|
+
import type {ObjectMerge} from 'type-fest';
|
|
37
|
+
|
|
38
|
+
const left: {a: string} = {a: '1'};
|
|
39
|
+
const right: {[x: string]: number} = {a: 1};
|
|
40
|
+
|
|
41
|
+
const inferred = {...left, ...right};
|
|
42
|
+
//=> {a: string}
|
|
43
|
+
|
|
44
|
+
inferred.a.toUpperCase(); // No compile time error, but fails at runtime.
|
|
45
|
+
|
|
46
|
+
const objectAssign = Object.assign(left, right);
|
|
47
|
+
//=> {a: string} & {[x: string]: number}
|
|
48
|
+
|
|
49
|
+
objectAssign.a.toUpperCase(); // No compile time error, but fails at runtime.
|
|
50
|
+
|
|
51
|
+
declare const objectMerge: ObjectMerge<typeof left, typeof right>;
|
|
52
|
+
//=> {[x: string]: string | number; a: string | number}
|
|
53
|
+
|
|
54
|
+
// @ts-expect-error
|
|
55
|
+
objectMerge.a.toUpperCase(); // Correctly errors at compile time.
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Can be used to merge generic type arguments.
|
|
59
|
+
|
|
60
|
+
In the following example, object spread without `ObjectMerge` produces an intersection type that is not particularly usable, whereas `ObjectMerge` produces a correctly merged and usable result.
|
|
61
|
+
|
|
62
|
+
@example
|
|
63
|
+
```ts
|
|
64
|
+
import type {ObjectMerge} from 'type-fest';
|
|
65
|
+
|
|
66
|
+
function withoutObjectMerge<T extends object, U extends object>(left: T, right: U) {
|
|
67
|
+
return {...left, ...right};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const result1 = withoutObjectMerge({a: 1}, {a: 'one'});
|
|
71
|
+
//=> {a: number} & {a: string}
|
|
72
|
+
|
|
73
|
+
const {a} = result1;
|
|
74
|
+
//=> never
|
|
75
|
+
|
|
76
|
+
function withObjectMerge<T extends object, U extends object>(left: T, right: U) {
|
|
77
|
+
return {...left, ...right} as unknown as ObjectMerge<T, U>;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const result2 = withObjectMerge({b: 1}, {b: 'one'});
|
|
81
|
+
//=> {b: string}
|
|
82
|
+
|
|
83
|
+
const {b} = result2;
|
|
84
|
+
//=> string
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Note: If you want a simple merge where properties from the second object always override properties from the first object without considering runtime implications, refer to the {@link Merge} type.
|
|
88
|
+
|
|
89
|
+
@see {@link Merge}
|
|
90
|
+
@category Object
|
|
91
|
+
*/
|
|
92
|
+
export type ObjectMerge<First extends object, Second extends object> =
|
|
93
|
+
IfNotAnyOrNever<First, IfNotAnyOrNever<Second, First extends unknown // For distributing `First`
|
|
94
|
+
? Second extends unknown // For distributing `Second`
|
|
95
|
+
? First extends MapsSetsOrArrays
|
|
96
|
+
? unknown
|
|
97
|
+
: Second extends MapsSetsOrArrays
|
|
98
|
+
? unknown
|
|
99
|
+
: _ObjectMerge<
|
|
100
|
+
First,
|
|
101
|
+
Second,
|
|
102
|
+
NormalizedLiteralKeys<First>,
|
|
103
|
+
NormalizedLiteralKeys<Second>,
|
|
104
|
+
IsExactOptionalPropertyTypesEnabled extends true ? Required<First> : First,
|
|
105
|
+
IsExactOptionalPropertyTypesEnabled extends true ? Required<Second> : Second
|
|
106
|
+
>
|
|
107
|
+
: never // Should never happen
|
|
108
|
+
: never>, First & Second>; // Should never happen
|
|
109
|
+
|
|
110
|
+
type _ObjectMerge<
|
|
111
|
+
First extends object,
|
|
112
|
+
Second extends object,
|
|
113
|
+
NormalizedFirstLiteralKeys extends PropertyKey,
|
|
114
|
+
NormalizedSecondLiteralKeys extends PropertyKey,
|
|
115
|
+
NormalizedFirst extends object,
|
|
116
|
+
NormalizedSecond extends object,
|
|
117
|
+
> = Simplify<{
|
|
118
|
+
// Map over literal keys of `Second`, except those that are optional and also present in `First`.
|
|
119
|
+
-readonly [P in keyof Second as P extends NormalizedSecondLiteralKeys
|
|
120
|
+
? P extends NormalizedFirstLiteralKeys
|
|
121
|
+
? If<IsOptionalKeyOf<Second, P>, never, P>
|
|
122
|
+
: P
|
|
123
|
+
: never]:
|
|
124
|
+
| Second[P]
|
|
125
|
+
| (P extends NormalizedKeys<keyof PickIndexSignature<First>>
|
|
126
|
+
? If<IsOptionalKeyOf<Second, P>, First[NormalizedKeys<P> & keyof First], never>
|
|
127
|
+
: never)
|
|
128
|
+
} & {
|
|
129
|
+
// Map over literal keys of `First`, except those that are not present in `Second`.
|
|
130
|
+
-readonly [P in keyof First as P extends NormalizedFirstLiteralKeys
|
|
131
|
+
? P extends NormalizedSecondLiteralKeys
|
|
132
|
+
? never
|
|
133
|
+
: P
|
|
134
|
+
: never]:
|
|
135
|
+
| First[P]
|
|
136
|
+
// If there's a matching index signature in `Second`, then add the type for it as well,
|
|
137
|
+
// for example, in `ObjectMerge<{a: string}, {[x: string]: number}>`, `a` is of type `string | number`.
|
|
138
|
+
| (P extends NormalizedKeys<keyof Second>
|
|
139
|
+
? Second[NormalizedKeys<P> & keyof Second]
|
|
140
|
+
: never);
|
|
141
|
+
} & {
|
|
142
|
+
// Map over non-literal keys of `Second`.
|
|
143
|
+
-readonly [P in keyof Second as P extends NormalizedSecondLiteralKeys ? never : P]:
|
|
144
|
+
| Second[P]
|
|
145
|
+
// If there's a matching key in `First`, then add the type for it as well,
|
|
146
|
+
// for example, in `ObjectMerge<{a: number}, {[x: string]: string}>`,
|
|
147
|
+
// the resulting type is `{[x: string]: number | string; a: number | string}`.
|
|
148
|
+
// But, exclude keys from `First` that would surely get overwritten,
|
|
149
|
+
// for example, in `ObjectMerge<{a: number}, {[x: string]: string; a: string}>`,
|
|
150
|
+
// `a` from `First` would get overwritten by `a` from `Second`, so don't add type for it.
|
|
151
|
+
| (NormalizedKeys<P> & Exclude<keyof First, NormalizedKeys<RequiredKeysOf<OmitIndexSignature<Second>>>> extends infer NonOverwrittenKeysOfFirst
|
|
152
|
+
? If<IsNever<NonOverwrittenKeysOfFirst>, // This check is required because indexing with `never` doesn't always yield `never`, for example, `{[x: string]: number}[never]` results in `number`.
|
|
153
|
+
never,
|
|
154
|
+
NormalizedFirst[NonOverwrittenKeysOfFirst & keyof NormalizedFirst]>
|
|
155
|
+
: never); // Should never happen
|
|
156
|
+
} & {
|
|
157
|
+
// Map over non-literal keys of `First`.
|
|
158
|
+
-readonly [P in keyof First as P extends NormalizedFirstLiteralKeys ? never : P]:
|
|
159
|
+
| First[P]
|
|
160
|
+
| If<IsNever<NormalizedKeys<P> & keyof Second>, // This check is required because indexing with `never` doesn't always yield `never`, for example, `{[x: string]: number}[never]` results in `number`.
|
|
161
|
+
never,
|
|
162
|
+
NormalizedSecond[NormalizedKeys<P> & keyof NormalizedSecond]>;
|
|
163
|
+
} & {
|
|
164
|
+
// Handle optional keys of `Second` that are also present in `First`.
|
|
165
|
+
// Map over `First` instead of `Second` because the modifier is in accordance with `First`.
|
|
166
|
+
-readonly [P in keyof First as P extends NormalizedFirstLiteralKeys
|
|
167
|
+
? P extends NormalizedSecondLiteralKeys
|
|
168
|
+
? If<IsOptionalKeyOf<Second, NormalizedKeys<P> & keyof Second>, P, never>
|
|
169
|
+
: never
|
|
170
|
+
: never]:
|
|
171
|
+
| First[P]
|
|
172
|
+
| NormalizedSecond[NormalizedKeys<P> & keyof NormalizedSecond]
|
|
173
|
+
}>;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
Get literal keys of a type, including both string and number representations wherever applicable.
|
|
177
|
+
|
|
178
|
+
@example
|
|
179
|
+
```ts
|
|
180
|
+
type A = NormalizedLiteralKeys<{0: string; '1'?: number; foo: boolean}>;
|
|
181
|
+
//=> 0 | '0' | 1 | '1' | 'foo'
|
|
182
|
+
|
|
183
|
+
type B = NormalizedLiteralKeys<{[x: string]: string | number; 0: string; '1'?: number}>;
|
|
184
|
+
//=> 0 | '0' | 1 | '1'
|
|
185
|
+
|
|
186
|
+
type C = NormalizedLiteralKeys<{[x: string]: unknown}>;
|
|
187
|
+
//=> never
|
|
188
|
+
```
|
|
189
|
+
*/
|
|
190
|
+
type NormalizedLiteralKeys<Type> = Type extends unknown // For distributing `Type`
|
|
191
|
+
? NormalizedKeys<keyof OmitIndexSignature<Type>>
|
|
192
|
+
: never; // Should never happen
|
|
193
|
+
|
|
194
|
+
export {};
|
package/source/omit-deep.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type {IsNever} from './is-never.d.ts';
|
|
|
5
5
|
import type {LiteralUnion} from './literal-union.d.ts';
|
|
6
6
|
import type {Paths} from './paths.d.ts';
|
|
7
7
|
import type {SimplifyDeep} from './simplify-deep.d.ts';
|
|
8
|
+
import type {Simplify} from './simplify.d.ts';
|
|
8
9
|
import type {UnionToTuple} from './union-to-tuple.d.ts';
|
|
9
10
|
import type {UnknownArray} from './unknown-array.d.ts';
|
|
10
11
|
|
|
@@ -33,11 +34,7 @@ type Info = {
|
|
|
33
34
|
};
|
|
34
35
|
|
|
35
36
|
type UsefulInfo = OmitDeep<Info, 'userInfo.uselessInfo'>;
|
|
36
|
-
//=> {
|
|
37
|
-
// userInfo: {
|
|
38
|
-
// name: string;
|
|
39
|
-
// };
|
|
40
|
-
// };
|
|
37
|
+
//=> {userInfo: {name: string}}
|
|
41
38
|
|
|
42
39
|
// Supports removing multiple paths
|
|
43
40
|
type Info1 = {
|
|
@@ -51,15 +48,11 @@ type Info1 = {
|
|
|
51
48
|
};
|
|
52
49
|
|
|
53
50
|
type UsefulInfo1 = OmitDeep<Info1, 'userInfo.uselessInfo' | 'userInfo.uselessField'>;
|
|
54
|
-
//=> {
|
|
55
|
-
// userInfo: {
|
|
56
|
-
// name: string;
|
|
57
|
-
// };
|
|
58
|
-
// };
|
|
51
|
+
//=> {userInfo: {name: string}}
|
|
59
52
|
|
|
60
53
|
// Supports array
|
|
61
54
|
type A = OmitDeep<[1, 'foo', 2], '1'>;
|
|
62
|
-
//=> [1, unknown, 2]
|
|
55
|
+
//=> [1, unknown, 2]
|
|
63
56
|
|
|
64
57
|
// Supports recursing into array
|
|
65
58
|
|
|
@@ -76,16 +69,7 @@ type Info2 = {
|
|
|
76
69
|
};
|
|
77
70
|
|
|
78
71
|
type AddressInfo = OmitDeep<Info2, 'address.1.foo'>;
|
|
79
|
-
//=> {
|
|
80
|
-
// address: [
|
|
81
|
-
// {
|
|
82
|
-
// street: string;
|
|
83
|
-
// },
|
|
84
|
-
// {
|
|
85
|
-
// street2: string;
|
|
86
|
-
// };
|
|
87
|
-
// ];
|
|
88
|
-
// };
|
|
72
|
+
//=> {address: [{street: string}, {street2: string}]}
|
|
89
73
|
```
|
|
90
74
|
|
|
91
75
|
@category Object
|
|
@@ -135,7 +119,7 @@ P extends `${infer RecordKeyInPath}.${infer SubPath}`
|
|
|
135
119
|
? IsNever<Key> extends true
|
|
136
120
|
? ObjectT
|
|
137
121
|
: Key extends PropertyKey
|
|
138
|
-
? Omit<ObjectT, Key
|
|
122
|
+
? Simplify<Omit<ObjectT, Key>> // `Simplify` to prevent `Omit` from appearing in the resulting type
|
|
139
123
|
: ObjectT
|
|
140
124
|
: ObjectT;
|
|
141
125
|
|
|
@@ -17,7 +17,7 @@ const indexed: Record<string, unknown> = {}; // Allowed
|
|
|
17
17
|
|
|
18
18
|
// @ts-expect-error
|
|
19
19
|
const keyed: Record<'foo', unknown> = {}; // Error
|
|
20
|
-
//
|
|
20
|
+
// TS2739: Type '{}' is missing the following properties from type 'Record<"foo" | "bar", unknown>': foo, bar
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
Instead of causing a type error like the above, you can also use a [conditional type](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html) to test whether a type is assignable to another:
|
|
@@ -26,12 +26,16 @@ Instead of causing a type error like the above, you can also use a [conditional
|
|
|
26
26
|
type Indexed = {} extends Record<string, unknown>
|
|
27
27
|
? '✅ `{}` is assignable to `Record<string, unknown>`'
|
|
28
28
|
: '❌ `{}` is NOT assignable to `Record<string, unknown>`';
|
|
29
|
-
|
|
29
|
+
|
|
30
|
+
type IndexedResult = Indexed;
|
|
31
|
+
//=> '✅ `{}` is assignable to `Record<string, unknown>`'
|
|
30
32
|
|
|
31
33
|
type Keyed = {} extends Record<'foo' | 'bar', unknown>
|
|
32
34
|
? '✅ `{}` is assignable to `Record<\'foo\' | \'bar\', unknown>`'
|
|
33
35
|
: '❌ `{}` is NOT assignable to `Record<\'foo\' | \'bar\', unknown>`';
|
|
34
|
-
|
|
36
|
+
|
|
37
|
+
type KeyedResult = Keyed;
|
|
38
|
+
//=> '❌ `{}` is NOT assignable to `Record<\'foo\' | \'bar\', unknown>`'
|
|
35
39
|
```
|
|
36
40
|
|
|
37
41
|
Using a [mapped type](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html#further-exploration), you can then check for each `KeyType` of `ObjectType`...
|
|
@@ -79,7 +83,7 @@ type Example = {
|
|
|
79
83
|
};
|
|
80
84
|
|
|
81
85
|
type ExampleWithoutIndexSignatures = OmitIndexSignature<Example>;
|
|
82
|
-
|
|
86
|
+
//=> {foo: 'bar'; qux?: 'baz'}
|
|
83
87
|
```
|
|
84
88
|
|
|
85
89
|
@see {@link PickIndexSignature}
|
package/source/paths.d.ts
CHANGED
|
@@ -92,7 +92,7 @@ export type PathsOptions = {
|
|
|
92
92
|
};
|
|
93
93
|
|
|
94
94
|
type AllPaths = Paths<ArrayExample, {leavesOnly: false}>;
|
|
95
|
-
//=> 'array' | `array.${number}` | `array.${number}.foo` | 'tuple
|
|
95
|
+
//=> 'array' | 'tuple' | `array.${number}` | `array.${number}.foo` | 'tuple.0' | 'tuple.1' | 'tuple.1.bar'
|
|
96
96
|
|
|
97
97
|
type LeafPaths = Paths<ArrayExample, {leavesOnly: true}>;
|
|
98
98
|
//=> `array.${number}.foo` | 'tuple.0' | 'tuple.1.bar'
|
|
@@ -18,11 +18,11 @@ const guestFencingTeam: FencingTeam = ['Josh', 'Michael', 'Robert'];
|
|
|
18
18
|
|
|
19
19
|
// @ts-expect-error
|
|
20
20
|
const homeFencingTeam: FencingTeam = ['George', 'John'];
|
|
21
|
-
|
|
21
|
+
// Error: Type '[string, string]' is not assignable to type 'readonly [string, string, string]'.
|
|
22
22
|
|
|
23
23
|
// @ts-expect-error
|
|
24
24
|
guestFencingTeam.push('Sam');
|
|
25
|
-
|
|
25
|
+
// Error: Property 'push' does not exist on type 'readonly [string, string, string]'.
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
@deprecated This type will be removed in the next major version. Use the built-in `Readonly` type in combination with the {@link TupleOf} type instead, like `Readonly<TupleOf<Length, Element>>`.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type {BuiltIns, HasMultipleCallSignatures} from './internal/index.d.ts';
|
|
2
2
|
import type {IsNever} from './is-never.d.ts';
|
|
3
|
+
import type {Simplify} from './simplify.d.ts';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
Create a type from another type with all keys and nested keys set to required.
|
|
@@ -65,7 +66,7 @@ export type RequiredDeep<T> = T extends BuiltIns
|
|
|
65
66
|
? T
|
|
66
67
|
: ((...arguments_: Parameters<T>) => ReturnType<T>) & RequiredObjectDeep<T>
|
|
67
68
|
: T extends object
|
|
68
|
-
? RequiredObjectDeep<T
|
|
69
|
+
? Simplify<RequiredObjectDeep<T>> // `Simplify` to prevent `RequiredObjectDeep` from appearing in the resulting type
|
|
69
70
|
: unknown;
|
|
70
71
|
|
|
71
72
|
type RequiredObjectDeep<ObjectType extends object> = {
|
package/source/schema.d.ts
CHANGED
|
@@ -24,15 +24,18 @@ export type SchemaOptions = {
|
|
|
24
24
|
|
|
25
25
|
type ParticipantsWithMetadata = Schema<Participants, {id: number; name: string}, {recurseIntoArrays: true}>;
|
|
26
26
|
//=> {
|
|
27
|
-
// attendees:
|
|
28
|
-
//
|
|
29
|
-
//
|
|
27
|
+
// attendees: {
|
|
28
|
+
// id: number;
|
|
29
|
+
// name: string;
|
|
30
|
+
// }[];
|
|
31
|
+
// speakers: {
|
|
32
|
+
// id: number;
|
|
33
|
+
// name: string;
|
|
34
|
+
// }[];
|
|
35
|
+
// }
|
|
30
36
|
|
|
31
37
|
type ParticipantsCount = Schema<Participants, number, {recurseIntoArrays: false}>;
|
|
32
|
-
//=> {
|
|
33
|
-
// attendees: number;
|
|
34
|
-
// speakers: number;
|
|
35
|
-
// };
|
|
38
|
+
//=> {attendees: number; speakers: number}
|
|
36
39
|
```
|
|
37
40
|
|
|
38
41
|
@default true
|
|
@@ -64,11 +64,11 @@ Note:
|
|
|
64
64
|
import type {SetParameterType} from 'type-fest';
|
|
65
65
|
|
|
66
66
|
const fn = (a: number) => a;
|
|
67
|
-
//=>
|
|
67
|
+
//=> (a: number) => number
|
|
68
68
|
|
|
69
69
|
// We change type of `a` to `string`, but return type is still `number`.
|
|
70
70
|
type Fn = SetParameterType<typeof fn, {0: string}>;
|
|
71
|
-
//=> (a: string) => number
|
|
71
|
+
//=> (a: string) => number
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
Use-case:
|
|
@@ -87,25 +87,25 @@ type Data = SuccessData | ErrorData;
|
|
|
87
87
|
type HandleMessage = (data: Data, message: string, ...arguments_: any[]) => void;
|
|
88
88
|
|
|
89
89
|
type HandleOk = SetParameterType<HandleMessage, {0: SuccessData; 1: 'ok'}>;
|
|
90
|
-
//=> (data: SuccessData, message: 'ok', ...arguments_: any[]) => void
|
|
90
|
+
//=> (data: SuccessData, message: 'ok', ...arguments_: any[]) => void
|
|
91
91
|
|
|
92
92
|
// Another way to define the parameters to replace.
|
|
93
93
|
type HandleError = SetParameterType<HandleMessage, [data: ErrorData, message: 'error']>;
|
|
94
|
-
//=> (data: ErrorData, message: 'error', ...arguments_: any[]) => void
|
|
94
|
+
//=> (data: ErrorData, message: 'error', ...arguments_: any[]) => void
|
|
95
95
|
|
|
96
96
|
// Change single parameter type.
|
|
97
97
|
type HandleWarn = SetParameterType<HandleMessage, {1: 'warn'}>;
|
|
98
|
-
//=> (data: Data, message: 'warn', ...arguments_: any[]) => void
|
|
98
|
+
//=> (data: Data, message: 'warn', ...arguments_: any[]) => void
|
|
99
99
|
|
|
100
100
|
// Change rest parameter type.
|
|
101
101
|
|
|
102
102
|
// Way 1: Input full parameter type.
|
|
103
103
|
type HandleLog = SetParameterType<HandleMessage, [data: Data, message: 'log', ...arguments_: string[]]>;
|
|
104
|
-
//=> (data: Data, message: 'log', ...arguments_: string[]) => void
|
|
104
|
+
//=> (data: Data, message: 'log', ...arguments_: string[]) => void
|
|
105
105
|
|
|
106
106
|
// Way 2: Input rest parameter type by Object index.
|
|
107
107
|
type HandleLog2 = SetParameterType<HandleMessage, {2: string}>;
|
|
108
|
-
//=> (data: Data, message: string, ...arguments_: string[]) => void
|
|
108
|
+
//=> (data: Data, message: string, ...arguments_: string[]) => void
|
|
109
109
|
```
|
|
110
110
|
|
|
111
111
|
@category Function
|
|
@@ -25,13 +25,7 @@ type Foo = {
|
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
type SomeRequiredDeep = SetRequiredDeep<Foo, 'a' | `c.${number}.d`>;
|
|
28
|
-
//=> {
|
|
29
|
-
// a: number; // Is now required
|
|
30
|
-
// b?: string;
|
|
31
|
-
// c?: {
|
|
32
|
-
// d: number; // Is now required
|
|
33
|
-
// }[];
|
|
34
|
-
// }
|
|
28
|
+
//=> {b?: string; c?: {d: number}[]; a: number}
|
|
35
29
|
|
|
36
30
|
// Set specific indices in an array to be required.
|
|
37
31
|
type ArrayExample = SetRequiredDeep<{a: [number?, number?, number?]}, 'a.0' | 'a.1'>;
|
|
@@ -12,7 +12,7 @@ import type {SetReturnType} from 'type-fest';
|
|
|
12
12
|
type MyFunctionThatCanThrow = (foo: string, bar: number) => boolean;
|
|
13
13
|
|
|
14
14
|
type MyWrappedFunction = SetReturnType<MyFunctionThatCanThrow, ReturnType<MyFunctionThatCanThrow> | undefined>;
|
|
15
|
-
//=> (foo: string, bar: number) => boolean | undefined
|
|
15
|
+
//=> (foo: string, bar: number) => boolean | undefined
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
@category Function
|
|
@@ -50,10 +50,10 @@ type T3 = SplitOnRestElement<[number, string?]>;
|
|
|
50
50
|
//=> [[number, string?], [], []]
|
|
51
51
|
|
|
52
52
|
type T4 = SplitOnRestElement<[number, string?], {preserveOptionalModifier: false}>;
|
|
53
|
-
//=> [[number, string], [], []]
|
|
53
|
+
//=> [[number, string], [], []]
|
|
54
54
|
|
|
55
55
|
type T5 = SplitOnRestElement<readonly [string?, ...number[]], {preserveOptionalModifier: false}>;
|
|
56
|
-
//=> readonly [[string], number[], []]
|
|
56
|
+
//=> readonly [[string], number[], []]
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
@see {@link ExtractRestElement}
|
package/source/subtract.d.ts
CHANGED
package/source/sum.d.ts
CHANGED
package/source/tuple-of.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ type ZeroToFour = Range<0, 5>;
|
|
|
30
30
|
//=> '0' | '1' | '2' | '3' | '4'
|
|
31
31
|
|
|
32
32
|
type ThreeToEight = Range<3, 9>;
|
|
33
|
-
//=> '
|
|
33
|
+
//=> '5' | '3' | '4' | '6' | '7' | '8'
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
Note: If the specified length is the non-literal `number` type, the result will not be a tuple but a regular array.
|