type-fest 4.32.0 → 4.34.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 +1 -0
- package/package.json +1 -1
- package/readme.md +2 -0
- package/source/all-union-fields.d.ts +88 -0
- package/source/except.d.ts +20 -0
- package/source/get.d.ts +1 -1
- package/source/internal/object.d.ts +37 -0
- package/source/internal/tuple.d.ts +5 -3
- package/source/is-literal.d.ts +39 -1
- package/source/keys-of-union.d.ts +5 -3
- package/source/omit-deep.d.ts +12 -4
- package/source/omit-index-signature.d.ts +0 -12
- package/source/paths.d.ts +52 -2
- package/source/pick-index-signature.d.ts +1 -53
- package/source/replace.d.ts +1 -1
- package/source/set-optional.d.ts +1 -1
- package/source/set-readonly.d.ts +1 -1
- package/source/set-required-deep.d.ts +33 -11
- package/source/set-required.d.ts +1 -1
- package/source/shared-union-fields.d.ts +1 -0
- package/source/split.d.ts +10 -4
- package/source/string-repeat.d.ts +16 -12
- package/source/string-slice.d.ts +1 -1
package/index.d.ts
CHANGED
|
@@ -130,6 +130,7 @@ export type {ArraySplice} from './source/array-splice';
|
|
|
130
130
|
export type {ArrayTail} from './source/array-tail';
|
|
131
131
|
export type {SetFieldType} from './source/set-field-type';
|
|
132
132
|
export type {Paths} from './source/paths';
|
|
133
|
+
export type {AllUnionFields} from './source/all-union-fields';
|
|
133
134
|
export type {SharedUnionFields} from './source/shared-union-fields';
|
|
134
135
|
export type {SharedUnionFieldsDeep} from './source/shared-union-fields-deep';
|
|
135
136
|
export type {IsNull} from './source/is-null';
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -203,6 +203,7 @@ Click the type names for complete docs.
|
|
|
203
203
|
- [`Paths`](source/paths.d.ts) - Generate a union of all possible paths to properties in the given object.
|
|
204
204
|
- [`SharedUnionFields`](source/shared-union-fields.d.ts) - Create a type with shared fields from a union of object types.
|
|
205
205
|
- [`SharedUnionFieldsDeep`](source/shared-union-fields-deep.d.ts) - Create a type with shared fields from a union of object types, deeply traversing nested structures.
|
|
206
|
+
- [`AllUnionFields`](source/all-union-fields.d.ts) - Create a type with all fields from a union of object types.
|
|
206
207
|
- [`DistributedOmit`](source/distributed-omit.d.ts) - Omits keys from a type, distributing the operation over a union.
|
|
207
208
|
- [`DistributedPick`](source/distributed-pick.d.ts) - Picks keys from a type, distributing the operation over a union.
|
|
208
209
|
- [`And`](source/and.d.ts) - Returns a boolean for whether two given types are both true.
|
|
@@ -370,6 +371,7 @@ type ShouldBeNever = IfAny<'not any', 'not never', 'never'>;
|
|
|
370
371
|
- `SetEntry` - See [`IterableElement`](source/iterable-element.d.ts)
|
|
371
372
|
- `SetValues` - See [`IterableElement`](source/iterable-element.d.ts)
|
|
372
373
|
- `PickByTypes` - See [`ConditionalPick`](source/conditional-pick.d.ts)
|
|
374
|
+
- `HomomorphicOmit` - See [`Except`](source/except.d.ts)
|
|
373
375
|
|
|
374
376
|
## Tips
|
|
375
377
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type {NonRecursiveType, ReadonlyKeysOfUnion, ValueOfUnion} from './internal';
|
|
2
|
+
import type {KeysOfUnion} from './keys-of-union';
|
|
3
|
+
import type {SharedUnionFields} from './shared-union-fields';
|
|
4
|
+
import type {Simplify} from './simplify';
|
|
5
|
+
import type {UnknownArray} from './unknown-array';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
Create a type with all fields from a union of object types.
|
|
9
|
+
|
|
10
|
+
Use-cases:
|
|
11
|
+
- You want a safe object type where each key exists in the union object.
|
|
12
|
+
|
|
13
|
+
@example
|
|
14
|
+
```
|
|
15
|
+
import type {AllUnionFields} from 'type-fest';
|
|
16
|
+
|
|
17
|
+
type Cat = {
|
|
18
|
+
name: string;
|
|
19
|
+
type: 'cat';
|
|
20
|
+
catType: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type Dog = {
|
|
24
|
+
name: string;
|
|
25
|
+
type: 'dog';
|
|
26
|
+
dogType: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function displayPetInfo(petInfo: Cat | Dog) {
|
|
30
|
+
// typeof petInfo =>
|
|
31
|
+
// {
|
|
32
|
+
// name: string;
|
|
33
|
+
// type: 'cat';
|
|
34
|
+
// catType: string;
|
|
35
|
+
// } | {
|
|
36
|
+
// name: string;
|
|
37
|
+
// type: 'dog';
|
|
38
|
+
// dogType: string;
|
|
39
|
+
// }
|
|
40
|
+
|
|
41
|
+
console.log('name: ', petInfo.name);
|
|
42
|
+
console.log('type: ', petInfo.type);
|
|
43
|
+
|
|
44
|
+
// TypeScript complains about `catType` and `dogType` not existing on type `Cat | Dog`.
|
|
45
|
+
console.log('animal type: ', petInfo.catType ?? petInfo.dogType);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function displayPetInfo(petInfo: AllUnionFields<Cat | Dog>) {
|
|
49
|
+
// typeof petInfo =>
|
|
50
|
+
// {
|
|
51
|
+
// name: string;
|
|
52
|
+
// type: 'cat' | 'dog';
|
|
53
|
+
// catType?: string;
|
|
54
|
+
// dogType?: string;
|
|
55
|
+
// }
|
|
56
|
+
|
|
57
|
+
console.log('name: ', petInfo.name);
|
|
58
|
+
console.log('type: ', petInfo.type);
|
|
59
|
+
|
|
60
|
+
// No TypeScript error.
|
|
61
|
+
console.log('animal type: ', petInfo.catType ?? petInfo.dogType);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
@see SharedUnionFields
|
|
66
|
+
|
|
67
|
+
@category Object
|
|
68
|
+
@category Union
|
|
69
|
+
*/
|
|
70
|
+
export type AllUnionFields<Union> =
|
|
71
|
+
Extract<Union, NonRecursiveType | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown> | UnknownArray> extends infer SkippedMembers
|
|
72
|
+
? Exclude<Union, SkippedMembers> extends infer RelevantMembers
|
|
73
|
+
?
|
|
74
|
+
| SkippedMembers
|
|
75
|
+
| Simplify<
|
|
76
|
+
// Include fields that are common in all union members
|
|
77
|
+
SharedUnionFields<RelevantMembers> &
|
|
78
|
+
// Include readonly fields present in any union member
|
|
79
|
+
{
|
|
80
|
+
readonly [P in ReadonlyKeysOfUnion<RelevantMembers>]?: ValueOfUnion<RelevantMembers, P & KeysOfUnion<RelevantMembers>>
|
|
81
|
+
} &
|
|
82
|
+
// Include remaining fields that are neither common nor readonly
|
|
83
|
+
{
|
|
84
|
+
[P in Exclude<KeysOfUnion<RelevantMembers>, ReadonlyKeysOfUnion<RelevantMembers> | keyof RelevantMembers>]?: ValueOfUnion<RelevantMembers, P>
|
|
85
|
+
}
|
|
86
|
+
>
|
|
87
|
+
: never
|
|
88
|
+
: never;
|
package/source/except.d.ts
CHANGED
|
@@ -69,6 +69,26 @@ type FooWithoutB = Except<Foo, 'b', {requireExactProps: true}>;
|
|
|
69
69
|
|
|
70
70
|
const fooWithoutB: FooWithoutB = {a: 1, b: '2'};
|
|
71
71
|
//=> errors at 'b': Type 'string' is not assignable to type 'undefined'.
|
|
72
|
+
|
|
73
|
+
// The `Omit` utility type doesn't work when omitting specific keys from objects containing index signatures.
|
|
74
|
+
|
|
75
|
+
// Consider the following example:
|
|
76
|
+
|
|
77
|
+
type UserData = {
|
|
78
|
+
[metadata: string]: string;
|
|
79
|
+
email: string;
|
|
80
|
+
name: string;
|
|
81
|
+
role: 'admin' | 'user';
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// `Omit` clearly doesn't behave as expected in this case:
|
|
85
|
+
type PostPayload = Omit<UserData, 'email'>;
|
|
86
|
+
//=> type PostPayload = { [x: string]: string; [x: number]: string; }
|
|
87
|
+
|
|
88
|
+
// In situations like this, `Except` works better.
|
|
89
|
+
// It simply removes the `email` key while preserving all the other keys.
|
|
90
|
+
type PostPayload = Except<UserData, 'email'>;
|
|
91
|
+
//=> type PostPayload = { [x: string]: string; name: string; role: 'admin' | 'user'; }
|
|
72
92
|
```
|
|
73
93
|
|
|
74
94
|
@category Object
|
package/source/get.d.ts
CHANGED
|
@@ -205,6 +205,6 @@ export type Get<
|
|
|
205
205
|
BaseType,
|
|
206
206
|
Path extends
|
|
207
207
|
| readonly string[]
|
|
208
|
-
| LiteralStringUnion<ToString<Paths<BaseType, {bracketNotation: false}> | Paths<BaseType, {bracketNotation: true}>>>,
|
|
208
|
+
| LiteralStringUnion<ToString<Paths<BaseType, {bracketNotation: false; maxRecursionDepth: 2}> | Paths<BaseType, {bracketNotation: true; maxRecursionDepth: 2}>>>,
|
|
209
209
|
Options extends GetOptions = {}> =
|
|
210
210
|
GetWithPath<BaseType, Path extends string ? ToPath<Path> : Path, Options>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type {Simplify} from '../simplify';
|
|
2
2
|
import type {UnknownArray} from '../unknown-array';
|
|
3
|
+
import type {IsEqual} from '../is-equal';
|
|
3
4
|
import type {KeysOfUnion} from '../keys-of-union';
|
|
4
5
|
import type {FilterDefinedKeys, FilterOptionalKeys} from './keys';
|
|
5
6
|
import type {NonRecursiveType} from './type';
|
|
@@ -122,3 +123,39 @@ type IndexSignature = HomomorphicPick<{[k: string]: unknown}, number>;
|
|
|
122
123
|
export type HomomorphicPick<T, Keys extends KeysOfUnion<T>> = {
|
|
123
124
|
[P in keyof T as Extract<P, Keys>]: T[P]
|
|
124
125
|
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
Extract all possible values for a given key from a union of object types.
|
|
129
|
+
|
|
130
|
+
@example
|
|
131
|
+
```
|
|
132
|
+
type Statuses = ValueOfUnion<{ id: 1, status: "open" } | { id: 2, status: "closed" }, "status">;
|
|
133
|
+
//=> "open" | "closed"
|
|
134
|
+
```
|
|
135
|
+
*/
|
|
136
|
+
export type ValueOfUnion<Union, Key extends KeysOfUnion<Union>> =
|
|
137
|
+
Union extends unknown ? Key extends keyof Union ? Union[Key] : never : never;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
Extract all readonly keys from a union of object types.
|
|
141
|
+
|
|
142
|
+
@example
|
|
143
|
+
```
|
|
144
|
+
type User = {
|
|
145
|
+
readonly id: string;
|
|
146
|
+
name: string;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
type Post = {
|
|
150
|
+
readonly id: string;
|
|
151
|
+
readonly author: string;
|
|
152
|
+
body: string;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
type ReadonlyKeys = ReadonlyKeysOfUnion<User | Post>;
|
|
156
|
+
//=> "id" | "author"
|
|
157
|
+
```
|
|
158
|
+
*/
|
|
159
|
+
export type ReadonlyKeysOfUnion<Union> = Union extends unknown ? keyof {
|
|
160
|
+
[Key in keyof Union as IsEqual<{[K in Key]: Union[Key]}, {readonly [K in Key]: Union[Key]}> extends true ? Key : never]: never
|
|
161
|
+
} : never;
|
|
@@ -37,9 +37,11 @@ If `<Fill>` is not provided, it will default to `unknown`.
|
|
|
37
37
|
|
|
38
38
|
@link https://itnext.io/implementing-arithmetic-within-typescripts-type-system-a1ef140a6f6f
|
|
39
39
|
*/
|
|
40
|
-
export type BuildTuple<L extends number, Fill = unknown, T extends readonly unknown[] = []> =
|
|
41
|
-
?
|
|
42
|
-
:
|
|
40
|
+
export type BuildTuple<L extends number, Fill = unknown, T extends readonly unknown[] = []> = number extends L
|
|
41
|
+
? Fill[]
|
|
42
|
+
: L extends T['length']
|
|
43
|
+
? T
|
|
44
|
+
: BuildTuple<L, Fill, [...T, Fill]>;
|
|
43
45
|
|
|
44
46
|
/**
|
|
45
47
|
Returns the maximum value from a tuple of integers.
|
package/source/is-literal.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type {Primitive} from './primitive';
|
|
|
2
2
|
import type {Numeric} from './numeric';
|
|
3
3
|
import type {IsNotFalse, IsPrimitive} from './internal';
|
|
4
4
|
import type {IsNever} from './is-never';
|
|
5
|
+
import type {IfNever} from './if-never';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
Returns a boolean for whether the given type `T` is the specified `LiteralType`.
|
|
@@ -65,6 +66,8 @@ Useful for:
|
|
|
65
66
|
- constraining strings to be a string literal
|
|
66
67
|
- type utilities, such as when constructing parsers and ASTs
|
|
67
68
|
|
|
69
|
+
The implementation of this type is inspired by the trick mentioned in this [StackOverflow answer](https://stackoverflow.com/a/68261113/420747).
|
|
70
|
+
|
|
68
71
|
@example
|
|
69
72
|
```
|
|
70
73
|
import type {IsStringLiteral} from 'type-fest';
|
|
@@ -80,10 +83,45 @@ const output = capitalize('hello, world!');
|
|
|
80
83
|
//=> 'Hello, world!'
|
|
81
84
|
```
|
|
82
85
|
|
|
86
|
+
@example
|
|
87
|
+
```
|
|
88
|
+
// String types with infinite set of possible values return `false`.
|
|
89
|
+
|
|
90
|
+
import type {IsStringLiteral} from 'type-fest';
|
|
91
|
+
|
|
92
|
+
type AllUppercaseStrings = IsStringLiteral<Uppercase<string>>;
|
|
93
|
+
//=> false
|
|
94
|
+
|
|
95
|
+
type StringsStartingWithOn = IsStringLiteral<`on${string}`>;
|
|
96
|
+
//=> false
|
|
97
|
+
|
|
98
|
+
// This behaviour is particularly useful in string manipulation utilities, as infinite string types often require separate handling.
|
|
99
|
+
|
|
100
|
+
type Length<S extends string, Counter extends never[] = []> =
|
|
101
|
+
IsStringLiteral<S> extends false
|
|
102
|
+
? number // return `number` for infinite string types
|
|
103
|
+
: S extends `${string}${infer Tail}`
|
|
104
|
+
? Length<Tail, [...Counter, never]>
|
|
105
|
+
: Counter['length'];
|
|
106
|
+
|
|
107
|
+
type L1 = Length<Lowercase<string>>;
|
|
108
|
+
//=> number
|
|
109
|
+
|
|
110
|
+
type L2 = Length<`${number}`>;
|
|
111
|
+
//=> number
|
|
112
|
+
```
|
|
113
|
+
|
|
83
114
|
@category Type Guard
|
|
84
115
|
@category Utilities
|
|
85
116
|
*/
|
|
86
|
-
export type IsStringLiteral<T> =
|
|
117
|
+
export type IsStringLiteral<T> = IfNever<T, false,
|
|
118
|
+
// If `T` is an infinite string type (e.g., `on${string}`), `Record<T, never>` produces an index signature,
|
|
119
|
+
// and since `{}` extends index signatures, the result becomes `false`.
|
|
120
|
+
T extends string
|
|
121
|
+
? {} extends Record<T, never>
|
|
122
|
+
? false
|
|
123
|
+
: true
|
|
124
|
+
: false>;
|
|
87
125
|
|
|
88
126
|
/**
|
|
89
127
|
Returns a boolean for whether the given type is a `number` or `bigint` [literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types).
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type {UnionToIntersection} from './union-to-intersection';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
Create a union of all keys from a given type, even those exclusive to specific union members.
|
|
3
5
|
|
|
@@ -35,6 +37,6 @@ type AllKeys = KeysOfUnion<Union>;
|
|
|
35
37
|
|
|
36
38
|
@category Object
|
|
37
39
|
*/
|
|
38
|
-
export type KeysOfUnion<ObjectType> =
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
export type KeysOfUnion<ObjectType> =
|
|
41
|
+
// Hack to fix https://github.com/sindresorhus/type-fest/issues/1008
|
|
42
|
+
keyof UnionToIntersection<ObjectType extends unknown ? Record<keyof ObjectType, never> : never>;
|
package/source/omit-deep.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import type {UnionToTuple} from 'expect-type';
|
|
1
2
|
import type {ArraySplice} from './array-splice';
|
|
2
3
|
import type {ExactKey, IsArrayReadonly, NonRecursiveType, SetArrayAccess, ToString} from './internal';
|
|
3
4
|
import type {IsEqual} from './is-equal';
|
|
4
5
|
import type {IsNever} from './is-never';
|
|
5
6
|
import type {LiteralUnion} from './literal-union';
|
|
6
7
|
import type {Paths} from './paths';
|
|
7
|
-
import type {SharedUnionFieldsDeep} from './shared-union-fields-deep';
|
|
8
8
|
import type {SimplifyDeep} from './simplify-deep';
|
|
9
9
|
import type {UnknownArray} from './unknown-array';
|
|
10
10
|
|
|
@@ -92,11 +92,19 @@ type AddressInfo = OmitDeep<Info1, 'address.1.foo'>;
|
|
|
92
92
|
*/
|
|
93
93
|
export type OmitDeep<T, PathUnion extends LiteralUnion<Paths<T>, string>> =
|
|
94
94
|
SimplifyDeep<
|
|
95
|
-
|
|
96
|
-
{[P in PathUnion]: OmitDeepWithOnePath<T, P>}[PathUnion]
|
|
97
|
-
>,
|
|
95
|
+
OmitDeepHelper<T, UnionToTuple<PathUnion>>,
|
|
98
96
|
UnknownArray>;
|
|
99
97
|
|
|
98
|
+
/**
|
|
99
|
+
Internal helper for {@link OmitDeep}.
|
|
100
|
+
|
|
101
|
+
Recursively transforms `T` by applying {@link OmitDeepWithOnePath} for each path in `PathTuple`.
|
|
102
|
+
*/
|
|
103
|
+
type OmitDeepHelper<T, PathTuple extends UnknownArray> =
|
|
104
|
+
PathTuple extends [infer Path, ...infer RestPaths]
|
|
105
|
+
? OmitDeepHelper<OmitDeepWithOnePath<T, Path & (string | number)>, RestPaths>
|
|
106
|
+
: T;
|
|
107
|
+
|
|
100
108
|
/**
|
|
101
109
|
Omit one path from the given object/array.
|
|
102
110
|
*/
|
|
@@ -61,18 +61,6 @@ type OmitIndexSignature<ObjectType> = {
|
|
|
61
61
|
|
|
62
62
|
If `{}` is assignable, it means that `KeyType` is an index signature and we want to remove it. If it is not assignable, `KeyType` is a "real" key and we want to keep it.
|
|
63
63
|
|
|
64
|
-
```
|
|
65
|
-
import type {OmitIndexSignature} from 'type-fest';
|
|
66
|
-
|
|
67
|
-
type OmitIndexSignature<ObjectType> = {
|
|
68
|
-
[KeyType in keyof ObjectType
|
|
69
|
-
as {} extends Record<KeyType, unknown>
|
|
70
|
-
? never // => Remove this `KeyType`.
|
|
71
|
-
: KeyType // => Keep this `KeyType` as it is.
|
|
72
|
-
]: ObjectType[KeyType];
|
|
73
|
-
};
|
|
74
|
-
```
|
|
75
|
-
|
|
76
64
|
@example
|
|
77
65
|
```
|
|
78
66
|
import type {OmitIndexSignature} from 'type-fest';
|
package/source/paths.d.ts
CHANGED
|
@@ -50,11 +50,53 @@ export type PathsOptions = {
|
|
|
50
50
|
```
|
|
51
51
|
*/
|
|
52
52
|
bracketNotation?: boolean;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
Only include leaf paths in the output.
|
|
56
|
+
|
|
57
|
+
@default false
|
|
58
|
+
|
|
59
|
+
@example
|
|
60
|
+
```
|
|
61
|
+
type Post = {
|
|
62
|
+
id: number;
|
|
63
|
+
author: {
|
|
64
|
+
id: number;
|
|
65
|
+
name: {
|
|
66
|
+
first: string;
|
|
67
|
+
last: string;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
type AllPaths = Paths<Post, {leavesOnly: false}>;
|
|
73
|
+
//=> 'id' | 'author' | 'author.id' | 'author.name' | 'author.name.first' | 'author.name.last'
|
|
74
|
+
|
|
75
|
+
type LeafPaths = Paths<Post, {leavesOnly: true}>;
|
|
76
|
+
//=> 'id' | 'author.id' | 'author.name.first' | 'author.name.last'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
@example
|
|
80
|
+
```
|
|
81
|
+
type ArrayExample = {
|
|
82
|
+
array: Array<{foo: string}>;
|
|
83
|
+
tuple: [string, {bar: string}];
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
type AllPaths = Paths<ArrayExample, {leavesOnly: false}>;
|
|
87
|
+
//=> 'array' | `array.${number}` | `array.${number}.foo` | 'tuple' | 'tuple.0' | 'tuple.1' | 'tuple.1.bar'
|
|
88
|
+
|
|
89
|
+
type LeafPaths = Paths<ArrayExample, {leavesOnly: true}>;
|
|
90
|
+
//=> `array.${number}.foo` | 'tuple.0' | 'tuple.1.bar'
|
|
91
|
+
```
|
|
92
|
+
*/
|
|
93
|
+
leavesOnly?: boolean;
|
|
53
94
|
};
|
|
54
95
|
|
|
55
96
|
type DefaultPathsOptions = {
|
|
56
97
|
maxRecursionDepth: 10;
|
|
57
98
|
bracketNotation: false;
|
|
99
|
+
leavesOnly: false;
|
|
58
100
|
};
|
|
59
101
|
|
|
60
102
|
/**
|
|
@@ -103,6 +145,8 @@ export type Paths<T, Options extends PathsOptions = {}> = _Paths<T, {
|
|
|
103
145
|
maxRecursionDepth: Options['maxRecursionDepth'] extends number ? Options['maxRecursionDepth'] : DefaultPathsOptions['maxRecursionDepth'];
|
|
104
146
|
// Set default bracketNotation to false
|
|
105
147
|
bracketNotation: Options['bracketNotation'] extends boolean ? Options['bracketNotation'] : DefaultPathsOptions['bracketNotation'];
|
|
148
|
+
// Set default leavesOnly to false
|
|
149
|
+
leavesOnly: Options['leavesOnly'] extends boolean ? Options['leavesOnly'] : DefaultPathsOptions['leavesOnly'];
|
|
106
150
|
}>;
|
|
107
151
|
|
|
108
152
|
type _Paths<T, Options extends Required<PathsOptions>> =
|
|
@@ -142,11 +186,17 @@ type InternalPaths<T, Options extends Required<PathsOptions>> =
|
|
|
142
186
|
) extends infer TranformedKey extends string | number ?
|
|
143
187
|
// 1. If style is 'a[0].b' and 'Key' is a numberlike value like 3 or '3', transform 'Key' to `[${Key}]`, else to `${Key}` | Key
|
|
144
188
|
// 2. If style is 'a.0.b', transform 'Key' to `${Key}` | Key
|
|
145
|
-
|
|
|
189
|
+
| (Options['leavesOnly'] extends true
|
|
190
|
+
? MaxDepth extends 0
|
|
191
|
+
? TranformedKey
|
|
192
|
+
: T[Key] extends EmptyObject | readonly [] | NonRecursiveType | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown>
|
|
193
|
+
? TranformedKey
|
|
194
|
+
: never
|
|
195
|
+
: TranformedKey)
|
|
146
196
|
| (
|
|
147
197
|
// Recursively generate paths for the current key
|
|
148
198
|
GreaterThan<MaxDepth, 0> extends true // Limit the depth to prevent infinite recursion
|
|
149
|
-
? _Paths<T[Key], {bracketNotation: Options['bracketNotation']; maxRecursionDepth: Subtract<MaxDepth, 1
|
|
199
|
+
? _Paths<T[Key], {bracketNotation: Options['bracketNotation']; maxRecursionDepth: Subtract<MaxDepth, 1>; leavesOnly: Options['leavesOnly']}> extends infer SubPath
|
|
150
200
|
? SubPath extends string | number
|
|
151
201
|
? (
|
|
152
202
|
Options['bracketNotation'] extends true
|
|
@@ -3,8 +3,6 @@ Pick only index signatures from the given object type, leaving out all explicitl
|
|
|
3
3
|
|
|
4
4
|
This is the counterpart of `OmitIndexSignature`.
|
|
5
5
|
|
|
6
|
-
When you use a type that will iterate through an object that has indexed keys and explicitly defined keys you end up with a type where only the indexed keys are kept. This is because `keyof` of an indexed type always returns `string | number | symbol`, because every key is possible in that object. With this type, you can save the indexed keys and reinject them later, like in the second example below.
|
|
7
|
-
|
|
8
6
|
@example
|
|
9
7
|
```
|
|
10
8
|
import type {PickIndexSignature} from 'type-fest';
|
|
@@ -23,7 +21,7 @@ type Example = {
|
|
|
23
21
|
[x: `embedded-${number}`]: string;
|
|
24
22
|
|
|
25
23
|
// These explicitly defined keys will be removed.
|
|
26
|
-
['
|
|
24
|
+
['kebab-case-key']: string;
|
|
27
25
|
[symbolKey]: string;
|
|
28
26
|
foo: 'bar';
|
|
29
27
|
qux?: 'baz';
|
|
@@ -42,56 +40,6 @@ type ExampleIndexSignature = PickIndexSignature<Example>;
|
|
|
42
40
|
// }
|
|
43
41
|
```
|
|
44
42
|
|
|
45
|
-
@example
|
|
46
|
-
```
|
|
47
|
-
import type {OmitIndexSignature, PickIndexSignature, Simplify} from 'type-fest';
|
|
48
|
-
|
|
49
|
-
type Foo = {
|
|
50
|
-
[x: string]: string;
|
|
51
|
-
foo: string;
|
|
52
|
-
bar: number;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
// Imagine that you want a new type `Bar` that comes from `Foo`.
|
|
56
|
-
// => {
|
|
57
|
-
// [x: string]: string;
|
|
58
|
-
// bar: number;
|
|
59
|
-
// };
|
|
60
|
-
|
|
61
|
-
type Bar = Omit<Foo, 'foo'>;
|
|
62
|
-
// This is not working because `Omit` returns only indexed keys.
|
|
63
|
-
// => {
|
|
64
|
-
// [x: string]: string;
|
|
65
|
-
// [x: number]: string;
|
|
66
|
-
// }
|
|
67
|
-
|
|
68
|
-
// One solution is to save the indexed signatures to new type.
|
|
69
|
-
type FooIndexSignature = PickIndexSignature<Foo>;
|
|
70
|
-
// => {
|
|
71
|
-
// [x: string]: string;
|
|
72
|
-
// }
|
|
73
|
-
|
|
74
|
-
// Get a new type without index signatures.
|
|
75
|
-
type FooWithoutIndexSignature = OmitIndexSignature<Foo>;
|
|
76
|
-
// => {
|
|
77
|
-
// foo: string;
|
|
78
|
-
// bar: number;
|
|
79
|
-
// }
|
|
80
|
-
|
|
81
|
-
// At this point we can use Omit to get our new type.
|
|
82
|
-
type BarWithoutIndexSignature = Omit<FooWithoutIndexSignature, 'foo'>;
|
|
83
|
-
// => {
|
|
84
|
-
// bar: number;
|
|
85
|
-
// }
|
|
86
|
-
|
|
87
|
-
// And finally we can merge back the indexed signatures.
|
|
88
|
-
type BarWithIndexSignature = Simplify<BarWithoutIndexSignature & FooIndexSignature>;
|
|
89
|
-
// => {
|
|
90
|
-
// [x: string]: string;
|
|
91
|
-
// bar: number;
|
|
92
|
-
// }
|
|
93
|
-
```
|
|
94
|
-
|
|
95
43
|
@see OmitIndexSignature
|
|
96
44
|
@category Object
|
|
97
45
|
*/
|
package/source/replace.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ type ReplaceOptions = {
|
|
|
6
6
|
Represents a string with some or all matches replaced by a replacement.
|
|
7
7
|
|
|
8
8
|
Use-case:
|
|
9
|
-
- `
|
|
9
|
+
- `kebab-case-path` to `dotted.path.notation`
|
|
10
10
|
- Changing date/time format: `01-08-2042` → `01/08/2042`
|
|
11
11
|
- Manipulation of type properties, for example, removal of prefixes
|
|
12
12
|
|
package/source/set-optional.d.ts
CHANGED
|
@@ -34,6 +34,6 @@ export type SetOptional<BaseType, Keys extends keyof BaseType> =
|
|
|
34
34
|
// Pick just the keys that are readonly from the base type.
|
|
35
35
|
Except<BaseType, Keys> &
|
|
36
36
|
// Pick the keys that should be mutable from the base type and make them mutable.
|
|
37
|
-
Partial<HomomorphicPick<BaseType, Keys
|
|
37
|
+
Partial<HomomorphicPick<BaseType, Keys>>
|
|
38
38
|
>
|
|
39
39
|
: never;
|
package/source/set-readonly.d.ts
CHANGED
|
@@ -35,6 +35,6 @@ export type SetReadonly<BaseType, Keys extends keyof BaseType> =
|
|
|
35
35
|
BaseType extends unknown
|
|
36
36
|
? Simplify<
|
|
37
37
|
Except<BaseType, Keys> &
|
|
38
|
-
Readonly<HomomorphicPick<BaseType, Keys
|
|
38
|
+
Readonly<HomomorphicPick<BaseType, Keys>>
|
|
39
39
|
>
|
|
40
40
|
: never;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import type {IsAny} from './is-any';
|
|
1
2
|
import type {NonRecursiveType, StringToNumber} from './internal';
|
|
2
3
|
import type {Paths} from './paths';
|
|
4
|
+
import type {SetRequired} from './set-required';
|
|
3
5
|
import type {SimplifyDeep} from './simplify-deep';
|
|
6
|
+
import type {UnionToTuple} from './union-to-tuple';
|
|
7
|
+
import type {RequiredDeep} from './required-deep';
|
|
4
8
|
import type {UnknownArray} from './unknown-array';
|
|
5
9
|
|
|
6
10
|
/**
|
|
@@ -28,19 +32,37 @@ type SomeRequiredDeep = SetRequiredDeep<Foo, 'a' | `c.${number}.d`>;
|
|
|
28
32
|
// d: number // Is now required
|
|
29
33
|
// }[]
|
|
30
34
|
// }
|
|
35
|
+
|
|
36
|
+
// Set specific indices in an array to be required.
|
|
37
|
+
type ArrayExample = SetRequiredDeep<{a: [number?, number?, number?]}, 'a.0' | 'a.1'>;
|
|
38
|
+
//=> {a: [number, number, number?]}
|
|
31
39
|
```
|
|
32
40
|
|
|
33
41
|
@category Object
|
|
34
42
|
*/
|
|
35
|
-
export type SetRequiredDeep<BaseType, KeyPaths extends Paths<BaseType>> =
|
|
36
|
-
BaseType
|
|
43
|
+
export type SetRequiredDeep<BaseType, KeyPaths extends Paths<BaseType>> = IsAny<KeyPaths> extends true
|
|
44
|
+
? SimplifyDeep<RequiredDeep<BaseType>>
|
|
45
|
+
: SetRequiredDeepHelper<BaseType, UnionToTuple<KeyPaths>>;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
Internal helper for {@link SetRequiredDeep}.
|
|
49
|
+
|
|
50
|
+
Recursively transforms the `BaseType` by applying {@link SetRequiredDeepSinglePath} for each path in `KeyPathsTuple`.
|
|
51
|
+
*/
|
|
52
|
+
type SetRequiredDeepHelper<BaseType, KeyPathsTuple extends UnknownArray> =
|
|
53
|
+
KeyPathsTuple extends [infer KeyPath, ...infer RestPaths]
|
|
54
|
+
? SetRequiredDeepHelper<SetRequiredDeepSinglePath<BaseType, KeyPath>, RestPaths>
|
|
55
|
+
: BaseType;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
Makes a single path required in `BaseType`.
|
|
59
|
+
*/
|
|
60
|
+
type SetRequiredDeepSinglePath<BaseType, KeyPath> = BaseType extends NonRecursiveType
|
|
37
61
|
? BaseType
|
|
38
|
-
:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
: SetRequiredDeep<BaseType[K], KeyPaths extends `${K & (string | number)}.${infer Rest extends Paths<BaseType[K]>}` ? Rest : never>
|
|
46
|
-
}>;
|
|
62
|
+
: KeyPath extends `${infer Property}.${infer RestPath}`
|
|
63
|
+
? {
|
|
64
|
+
[Key in keyof BaseType]: Property extends `${Key & (string | number)}`
|
|
65
|
+
? SetRequiredDeepSinglePath<BaseType[Key], RestPath>
|
|
66
|
+
: BaseType[Key];
|
|
67
|
+
}
|
|
68
|
+
: SetRequired<BaseType, (KeyPath | StringToNumber<KeyPath & string>) & keyof BaseType>;
|
package/source/set-required.d.ts
CHANGED
|
@@ -43,7 +43,7 @@ export type SetRequired<BaseType, Keys extends keyof BaseType> =
|
|
|
43
43
|
// Pick just the keys that are optional from the base type.
|
|
44
44
|
Except<BaseType, Keys> &
|
|
45
45
|
// Pick the keys that should be required from the base type and make them required.
|
|
46
|
-
Required<HomomorphicPick<BaseType, Keys
|
|
46
|
+
Required<HomomorphicPick<BaseType, Keys>>
|
|
47
47
|
>;
|
|
48
48
|
|
|
49
49
|
/**
|
package/source/split.d.ts
CHANGED
|
@@ -22,8 +22,14 @@ array = split(items, ',');
|
|
|
22
22
|
export type Split<
|
|
23
23
|
S extends string,
|
|
24
24
|
Delimiter extends string,
|
|
25
|
+
> = SplitHelper<S, Delimiter>;
|
|
26
|
+
|
|
27
|
+
type SplitHelper<
|
|
28
|
+
S extends string,
|
|
29
|
+
Delimiter extends string,
|
|
30
|
+
Accumulator extends string[] = [],
|
|
25
31
|
> = S extends `${infer Head}${Delimiter}${infer Tail}`
|
|
26
|
-
?
|
|
27
|
-
:
|
|
28
|
-
?
|
|
29
|
-
: [S];
|
|
32
|
+
? SplitHelper<Tail, Delimiter, [...Accumulator, Head]>
|
|
33
|
+
: Delimiter extends ''
|
|
34
|
+
? Accumulator
|
|
35
|
+
: [...Accumulator, S];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import type {IsNumericLiteral} from './is-literal';
|
|
1
2
|
import type {IsNegative} from './numeric';
|
|
2
|
-
import type {Subtract} from './subtract';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
Returns a new string which contains the specified number of copies of a given string, just like `String#repeat()`.
|
|
@@ -28,16 +28,20 @@ stringRepeat('=', 3);
|
|
|
28
28
|
export type StringRepeat<
|
|
29
29
|
Input extends string,
|
|
30
30
|
Count extends number,
|
|
31
|
-
> =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
> = StringRepeatHelper<Input, Count>;
|
|
32
|
+
|
|
33
|
+
type StringRepeatHelper<
|
|
34
|
+
Input extends string,
|
|
35
|
+
Count extends number,
|
|
36
|
+
Counter extends never[] = [],
|
|
37
|
+
Accumulator extends string = '',
|
|
38
|
+
> =
|
|
39
|
+
IsNegative<Count> extends true
|
|
36
40
|
? never
|
|
37
|
-
:
|
|
41
|
+
: Input extends ''
|
|
38
42
|
? ''
|
|
39
|
-
:
|
|
40
|
-
?
|
|
41
|
-
:
|
|
42
|
-
?
|
|
43
|
-
: never
|
|
43
|
+
: Count extends Counter['length']
|
|
44
|
+
? Accumulator
|
|
45
|
+
: IsNumericLiteral<Count> extends false
|
|
46
|
+
? string
|
|
47
|
+
: StringRepeatHelper<Input, Count, [...Counter, never], `${Accumulator}${Input}`>;
|
package/source/string-slice.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export type StringSlice<
|
|
|
31
31
|
Start extends number = 0,
|
|
32
32
|
End extends number = StringToArray<S>['length'],
|
|
33
33
|
> = string extends S
|
|
34
|
-
? string
|
|
34
|
+
? string
|
|
35
35
|
: ArraySlice<StringToArray<S>, Start, End> extends infer R extends readonly string[]
|
|
36
36
|
? Join<R, ''>
|
|
37
37
|
: never;
|