type-fest 2.15.0 → 2.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -14,7 +14,7 @@ export {RequireAtLeastOne} from './source/require-at-least-one';
14
14
  export {RequireExactlyOne} from './source/require-exactly-one';
15
15
  export {RequireAllOrNone} from './source/require-all-or-none';
16
16
  export {RemoveIndexSignature} from './source/remove-index-signature';
17
- export {PartialDeep} from './source/partial-deep';
17
+ export {PartialDeep, PartialDeepOptions} from './source/partial-deep';
18
18
  export {ReadonlyDeep} from './source/readonly-deep';
19
19
  export {LiteralUnion} from './source/literal-union';
20
20
  export {Promisable} from './source/promisable';
@@ -38,7 +38,7 @@ export {Entry} from './source/entry';
38
38
  export {Entries} from './source/entries';
39
39
  export {SetReturnType} from './source/set-return-type';
40
40
  export {Asyncify} from './source/asyncify';
41
- export {Simplify} from './source/simplify';
41
+ export {Simplify, SimplifyOptions} from './source/simplify';
42
42
  export {Jsonify} from './source/jsonify';
43
43
  export {Schema} from './source/schema';
44
44
  export {LiteralToPrimitive} from './source/literal-to-primitive';
@@ -57,6 +57,10 @@ export {
57
57
  export {StringKeyOf} from './source/string-key-of';
58
58
  export {Exact} from './source/exact';
59
59
  export {ReadonlyTuple} from './source/readonly-tuple';
60
+ export {OptionalKeysOf} from './source/optional-keys-of';
61
+ export {HasOptionalKeys} from './source/has-optional-keys';
62
+ export {RequiredKeysOf} from './source/required-keys-of';
63
+ export {HasRequiredKeys} from './source/has-required-keys';
60
64
 
61
65
  // Template literal types
62
66
  export {CamelCase} from './source/camel-case';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "type-fest",
3
- "version": "2.15.0",
3
+ "version": "2.17.0",
4
4
  "description": "A collection of essential TypeScript types",
5
5
  "license": "(MIT OR CC0-1.0)",
6
6
  "repository": "sindresorhus/type-fest",
package/readme.md CHANGED
@@ -192,6 +192,10 @@ Click the type names for complete docs.
192
192
  - [`StringKeyOf`](source/string-key-of.d.ts) - Get keys of the given type as strings.
193
193
  - [`Schema`](source/schema.d.ts) - Create a deep version of another object type where property values are recursively replaced into a given value type.
194
194
  - [`Exact`](source/exact.d.ts) - Create a type that does not allow extra properties.
195
+ - [`OptionalKeysOf`](source/optional-keys-of.d.ts) - Extract all optional keys from the given type.
196
+ - [`HasOptionalKeys`](source/has-optional-keys.d.ts) - Create a `true`/`false` type depending on whether the given type has any optional fields.
197
+ - [`RequiredKeysOf`](source/required-keys-of.d.ts) - Extract all required keys from the given type.
198
+ - [`HasRequiredKeys`](source/has-required-keys.d.ts) - Create a `true`/`false` type depending on whether the given type has any required fields.
195
199
 
196
200
  ### JSON
197
201
 
@@ -835,7 +839,7 @@ There are many advanced types most users don't know about.
835
839
  type T2 = Uppercase<'foo' | 'bar'>; // 'FOO' | 'BAR'
836
840
 
837
841
  type T3<S extends string> = Uppercase<`aB${S}`>;
838
- type T4 = T30<'xYz'>; // 'ABXYZ'
842
+ type T4 = T3<'xYz'>; // 'ABXYZ'
839
843
 
840
844
  type T5 = Uppercase<string>; // string
841
845
  type T6 = Uppercase<any>; // any
@@ -856,7 +860,7 @@ There are many advanced types most users don't know about.
856
860
  type T2 = Lowercase<'FOO' | 'BAR'>; // 'foo' | 'bar'
857
861
 
858
862
  type T3<S extends string> = Lowercase<`aB${S}`>;
859
- type T4 = T32<'xYz'>; // 'abxyz'
863
+ type T4 = T3<'xYz'>; // 'abxyz'
860
864
 
861
865
  type T5 = Lowercase<string>; // string
862
866
  type T6 = Lowercase<any>; // any
@@ -877,7 +881,7 @@ There are many advanced types most users don't know about.
877
881
  type T2 = Capitalize<'foo' | 'bar'>; // 'Foo' | 'Bar'
878
882
 
879
883
  type T3<S extends string> = Capitalize<`aB${S}`>;
880
- type T4 = T32<'xYz'>; // 'ABxYz'
884
+ type T4 = T3<'xYz'>; // 'ABxYz'
881
885
 
882
886
  type T5 = Capitalize<string>; // string
883
887
  type T6 = Capitalize<any>; // any
@@ -898,7 +902,7 @@ There are many advanced types most users don't know about.
898
902
  type T2 = Uncapitalize<'Foo' | 'Bar'>; // 'foo' | 'bar'
899
903
 
900
904
  type T3<S extends string> = Uncapitalize<`AB${S}`>;
901
- type T4 = T30<'xYz'>; // 'aBxYz'
905
+ type T4 = T3<'xYz'>; // 'aBxYz'
902
906
 
903
907
  type T5 = Uncapitalize<string>; // string
904
908
  type T6 = Uncapitalize<any>; // any
@@ -1,4 +1,4 @@
1
- import type {WordSeparators} from '../source/utilities';
1
+ import type {WordSeparators} from '../source/internal';
2
2
  import type {Split} from './split';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import type {UpperCaseCharacters, WordSeparators} from '../source/utilities';
1
+ import type {UpperCaseCharacters, WordSeparators} from '../source/internal';
2
2
 
3
3
  /**
4
4
  Unlike a simpler split, this one includes the delimiter splitted on in the resulting array literal. This is to enable splitting on, for example, upper-case characters.
package/source/get.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type {StringDigit} from '../source/utilities';
1
+ import type {StringDigit} from '../source/internal';
2
2
  import type {Split} from './split';
3
3
  import type {StringKeyOf} from './string-key-of';
4
4
 
@@ -0,0 +1,21 @@
1
+ import {OptionalKeysOf} from './optional-keys-of';
2
+
3
+ /**
4
+ Creates a type that represents `true` or `false` depending on whether the given type has any optional fields.
5
+
6
+ This is useful when you want to create an API whose behavior depends on the presence or absence of optional fields.
7
+
8
+ @example
9
+ ```
10
+ import type {HasOptionalKeys, OptionalKeysOf} from 'type-fest';
11
+
12
+ type UpdateService<Entity extends object> = {
13
+ removeField: HasOptionalKeys<Entity> extends true
14
+ ? (field: OptionalKeysOf<Entity>) => Promise<void>
15
+ : never
16
+ }
17
+ ```
18
+
19
+ @category Utilities
20
+ */
21
+ export type HasOptionalKeys<BaseType extends object> = OptionalKeysOf<BaseType> extends never ? false : true;
@@ -0,0 +1,59 @@
1
+ import {RequiredKeysOf} from './required-keys-of';
2
+
3
+ /**
4
+ Creates a type that represents `true` or `false` depending on whether the given type has any required fields.
5
+
6
+ This is useful when you want to create an API whose behavior depends on the presence or absence of required fields.
7
+
8
+ @example
9
+ ```
10
+ import type {HasRequiredKeys} from 'type-fest';
11
+
12
+ type GeneratorOptions<Template extends object> = {
13
+ prop1: number;
14
+ prop2: string;
15
+ } & (HasRequiredKeys<Template> extends true
16
+ ? {template: Template}
17
+ : {template?: Template});
18
+
19
+ interface Template1 {
20
+ optionalSubParam?: string;
21
+ }
22
+
23
+ interface Template2 {
24
+ requiredSubParam: string;
25
+ }
26
+
27
+ type Options1 = GeneratorOptions<Template1>;
28
+ type Options2 = GeneratorOptions<Template2>;
29
+
30
+ const optA: Options1 = {
31
+ prop1: 0,
32
+ prop2: 'hi'
33
+ };
34
+ const optB: Options1 = {
35
+ prop1: 0,
36
+ prop2: 'hi',
37
+ template: {}
38
+ };
39
+ const optC: Options1 = {
40
+ prop1: 0,
41
+ prop2: 'hi',
42
+ template: {
43
+ optionalSubParam: 'optional value'
44
+ }
45
+ };
46
+
47
+ const optD: Options2 = {
48
+ prop1: 0,
49
+ prop2: 'hi',
50
+ template: {
51
+ requiredSubParam: 'required value'
52
+ }
53
+ };
54
+
55
+ ```
56
+
57
+ @category Utilities
58
+ */
59
+ export type HasRequiredKeys<BaseType extends object> = RequiredKeysOf<BaseType> extends never ? false : true;
@@ -15,8 +15,8 @@ type hasRed<array extends any[]> = Includes<array, 'red'>;
15
15
  @category Array
16
16
  */
17
17
  export type Includes<Value extends readonly any[], Item> =
18
- IsEqual<Value[0], Item> extends true
19
- ? true
20
- : Value extends [Value[0], ...infer rest]
21
- ? Includes<rest, Item>
22
- : false;
18
+ Value extends readonly [Value[0], ...infer rest]
19
+ ? IsEqual<Value[0], Item> extends true
20
+ ? true
21
+ : Includes<rest, Item>
22
+ : false;
@@ -51,3 +51,9 @@ The reason a simple `keyof Union` does not work is because `keyof` always return
51
51
  @link https://stackoverflow.com/a/49402091
52
52
  */
53
53
  export type KeysOfUnion<T> = T extends T ? keyof T : never;
54
+
55
+ export type UpperCaseCharacters = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z';
56
+
57
+ export type WordSeparators = '-' | '_' | ' ';
58
+
59
+ export type StringDigit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
@@ -66,7 +66,7 @@ type Jsonify<T> =
66
66
  // Check if there are any non-JSONable types represented in the union.
67
67
  // Note: The use of tuples in this first condition side-steps distributive conditional types
68
68
  // (see https://github.com/microsoft/TypeScript/issues/29368#issuecomment-453529532)
69
- [Extract<T, NotJsonable | BigInt>] extends [never]
69
+ [Extract<T, NotJsonable | bigint>] extends [never]
70
70
  ? T extends PositiveInfinity | NegativeInfinity ? null
71
71
  : T extends JsonPrimitive
72
72
  ? T // Primitive is acceptable
@@ -0,0 +1,38 @@
1
+ /**
2
+ Extract all optional keys from the given type.
3
+
4
+ This is useful when you want to create a new type that contains different type values for the optional keys only.
5
+
6
+ @example
7
+ ```
8
+ import type {OptionalKeysOf, Except} from 'type-fest';
9
+
10
+ interface User {
11
+ name: string;
12
+ surname: string;
13
+
14
+ luckyNumber?: number;
15
+ }
16
+
17
+ const REMOVE_FIELD = Symbol('remove field symbol');
18
+ type UpdateOperation<Entity extends object> = Except<Partial<Entity>, OptionalKeysOf<Entity>> & {
19
+ [Key in OptionalKeysOf<Entity>]?: Entity[Key] | typeof REMOVE_FIELD;
20
+ };
21
+
22
+ const update1: UpdateOperation<User> = {
23
+ name: 'Alice'
24
+ };
25
+
26
+ const update2: UpdateOperation<User> = {
27
+ name: 'Bob',
28
+ luckyNumber: REMOVE_FIELD
29
+ };
30
+ ```
31
+
32
+ @category Utilities
33
+ */
34
+ export type OptionalKeysOf<BaseType extends object> = Exclude<{
35
+ [Key in keyof BaseType]: BaseType extends Record<Key, BaseType[Key]>
36
+ ? never
37
+ : Key
38
+ }[keyof BaseType], undefined>;
@@ -1,5 +1,17 @@
1
1
  import type {BuiltIns} from './internal';
2
2
 
3
+ /**
4
+ @see PartialDeep
5
+ */
6
+ export interface PartialDeepOptions {
7
+ /**
8
+ Whether to affect the individual elements of arrays and tuples.
9
+
10
+ @default true
11
+ */
12
+ readonly recurseIntoArrays?: boolean;
13
+ }
14
+
3
15
  /**
4
16
  Create a type from another type with all keys and nested keys set to optional.
5
17
 
@@ -28,54 +40,74 @@ const applySavedSettings = (savedSettings: PartialDeep<Settings>) => {
28
40
  settings = applySavedSettings({textEditor: {fontWeight: 500}});
29
41
  ```
30
42
 
43
+ By default, this also affects array and tuple types:
44
+
45
+ ```
46
+ import type {PartialDeep} from 'type-fest';
47
+
48
+ interface Settings {
49
+ languages: string[];
50
+ }
51
+
52
+ const partialSettings: PartialDeep<Settings> = {
53
+ languages: [undefined]
54
+ };
55
+ ```
56
+
57
+ If this is undesirable, you can pass `{recurseIntoArrays: false}` as the second type argument.
58
+
31
59
  @category Object
32
60
  @category Array
33
61
  @category Set
34
62
  @category Map
35
63
  */
36
- export type PartialDeep<T> = T extends BuiltIns
64
+ export type PartialDeep<T, Options extends PartialDeepOptions = {}> = T extends BuiltIns
37
65
  ? T
38
66
  : T extends Map<infer KeyType, infer ValueType>
39
- ? PartialMapDeep<KeyType, ValueType>
67
+ ? PartialMapDeep<KeyType, ValueType, Options>
40
68
  : T extends Set<infer ItemType>
41
- ? PartialSetDeep<ItemType>
69
+ ? PartialSetDeep<ItemType, Options>
42
70
  : T extends ReadonlyMap<infer KeyType, infer ValueType>
43
- ? PartialReadonlyMapDeep<KeyType, ValueType>
71
+ ? PartialReadonlyMapDeep<KeyType, ValueType, Options>
44
72
  : T extends ReadonlySet<infer ItemType>
45
- ? PartialReadonlySetDeep<ItemType>
73
+ ? PartialReadonlySetDeep<ItemType, Options>
46
74
  : T extends ((...arguments: any[]) => unknown)
47
75
  ? T | undefined
48
76
  : T extends object
49
- ? T extends Array<infer ItemType> // Test for arrays/tuples, per https://github.com/microsoft/TypeScript/issues/35156
50
- ? ItemType[] extends T // Test for arrays (non-tuples) specifically
51
- ? Array<PartialDeep<ItemType | undefined>> // Recreate relevant array type to prevent eager evaluation of circular reference
52
- : PartialObjectDeep<T> // Tuples behave properly
53
- : PartialObjectDeep<T>
77
+ ? T extends ReadonlyArray<infer ItemType> // Test for arrays/tuples, per https://github.com/microsoft/TypeScript/issues/35156
78
+ ? Options['recurseIntoArrays'] extends false // If they opt out of array testing, just use the original type
79
+ ? T
80
+ : ItemType[] extends T // Test for arrays (non-tuples) specifically
81
+ ? readonly ItemType[] extends T // Differentiate readonly and mutable arrays
82
+ ? ReadonlyArray<PartialDeep<ItemType | undefined, Options>>
83
+ : Array<PartialDeep<ItemType | undefined, Options>>
84
+ : PartialObjectDeep<T, Options> // Tuples behave properly
85
+ : PartialObjectDeep<T, Options>
54
86
  : unknown;
55
87
 
56
88
  /**
57
89
  Same as `PartialDeep`, but accepts only `Map`s and as inputs. Internal helper for `PartialDeep`.
58
90
  */
59
- interface PartialMapDeep<KeyType, ValueType> extends Map<PartialDeep<KeyType>, PartialDeep<ValueType>> {}
91
+ interface PartialMapDeep<KeyType, ValueType, Options extends PartialDeepOptions> extends Map<PartialDeep<KeyType, Options>, PartialDeep<ValueType, Options>> {}
60
92
 
61
93
  /**
62
94
  Same as `PartialDeep`, but accepts only `Set`s as inputs. Internal helper for `PartialDeep`.
63
95
  */
64
- interface PartialSetDeep<T> extends Set<PartialDeep<T>> {}
96
+ interface PartialSetDeep<T, Options extends PartialDeepOptions> extends Set<PartialDeep<T, Options>> {}
65
97
 
66
98
  /**
67
99
  Same as `PartialDeep`, but accepts only `ReadonlyMap`s as inputs. Internal helper for `PartialDeep`.
68
100
  */
69
- interface PartialReadonlyMapDeep<KeyType, ValueType> extends ReadonlyMap<PartialDeep<KeyType>, PartialDeep<ValueType>> {}
101
+ interface PartialReadonlyMapDeep<KeyType, ValueType, Options extends PartialDeepOptions> extends ReadonlyMap<PartialDeep<KeyType, Options>, PartialDeep<ValueType, Options>> {}
70
102
 
71
103
  /**
72
104
  Same as `PartialDeep`, but accepts only `ReadonlySet`s as inputs. Internal helper for `PartialDeep`.
73
105
  */
74
- interface PartialReadonlySetDeep<T> extends ReadonlySet<PartialDeep<T>> {}
106
+ interface PartialReadonlySetDeep<T, Options extends PartialDeepOptions> extends ReadonlySet<PartialDeep<T, Options>> {}
75
107
 
76
108
  /**
77
109
  Same as `PartialDeep`, but accepts only `object`s as inputs. Internal helper for `PartialDeep`.
78
110
  */
79
- type PartialObjectDeep<ObjectType extends object> = {
80
- [KeyType in keyof ObjectType]?: PartialDeep<ObjectType[KeyType]>
111
+ type PartialObjectDeep<ObjectType extends object, Options extends PartialDeepOptions> = {
112
+ [KeyType in keyof ObjectType]?: PartialDeep<ObjectType[KeyType], Options>
81
113
  };
@@ -62,6 +62,6 @@ export type Replace<
62
62
  Options extends ReplaceOptions = {},
63
63
  > = Input extends `${infer Head}${Search}${infer Tail}`
64
64
  ? Options['all'] extends true
65
- ? Replace<`${Head}${Replacement}${Tail}`, Search, Replacement, Options>
65
+ ? `${Head}${Replacement}${Replace<Tail, Search, Replacement, Options>}`
66
66
  : `${Head}${Replacement}${Tail}`
67
67
  : Input;
@@ -0,0 +1,29 @@
1
+ /**
2
+ Extract all required keys from the given type.
3
+
4
+ This is useful when you want to create a new type that contains different type values for the required keys only or use the list of keys for validation purposes, etc...
5
+
6
+ @example
7
+ ```
8
+ import type {RequiredKeysOf} from 'type-fest';
9
+
10
+ declare function createValidation<Entity extends object, Key extends RequiredKeysOf<Entity> = RequiredKeysOf<Entity>>(field: Key, validator: (value: Entity[Key]) => boolean): ValidatorFn;
11
+
12
+ interface User {
13
+ name: string;
14
+ surname: string;
15
+
16
+ luckyNumber?: number;
17
+ }
18
+
19
+ const validator1 = createValidation<User>('name', value => value.length < 25);
20
+ const validator2 = createValidation<User>('surname', value => value.length < 25);
21
+ ```
22
+
23
+ @category Utilities
24
+ */
25
+ export type RequiredKeysOf<BaseType extends object> = Exclude<{
26
+ [Key in keyof BaseType]: BaseType extends Record<Key, BaseType[Key]>
27
+ ? Key
28
+ : never
29
+ }[keyof BaseType], undefined>;
@@ -1,3 +1,23 @@
1
+ /**
2
+ @see Simplify
3
+ */
4
+ export interface SimplifyOptions {
5
+ /**
6
+ Do the simplification recursively.
7
+
8
+ @default false
9
+ */
10
+ deep?: boolean;
11
+ }
12
+
13
+ // Flatten a type without worrying about the result.
14
+ type Flatten<
15
+ AnyType,
16
+ Options extends SimplifyOptions = {},
17
+ > = Options['deep'] extends true
18
+ ? {[KeyType in keyof AnyType]: Simplify<AnyType[KeyType], Options>}
19
+ : {[KeyType in keyof AnyType]: AnyType[KeyType]};
20
+
1
21
  /**
2
22
  Useful to flatten the type output to improve type hints shown in editors. And also to transform an interface into a type to aide with assignability.
3
23
 
@@ -55,4 +75,9 @@ fn(someInterface as Simplify<SomeInterface>); // Good: transform an `interface`
55
75
 
56
76
  @category Object
57
77
  */
58
- export type Simplify<T> = {[KeyType in keyof T]: T[KeyType]};
78
+ export type Simplify<
79
+ AnyType,
80
+ Options extends SimplifyOptions = {},
81
+ > = Flatten<AnyType> extends AnyType
82
+ ? Flatten<AnyType, Options>
83
+ : AnyType;
@@ -1,5 +0,0 @@
1
- export type UpperCaseCharacters = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z';
2
-
3
- export type WordSeparators = '-' | '_' | ' ';
4
-
5
- export type StringDigit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';