ts-gems 3.12.0 → 4.0.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.
@@ -0,0 +1,238 @@
1
+ # Base Types
2
+
3
+ Source: [`lib/types.d.ts`](../../lib/types.d.ts)
4
+
5
+ Foundational type aliases used throughout the rest of the library, and handy
6
+ on their own.
7
+
8
+ ## `Primitive`
9
+
10
+ ```ts
11
+ type Primitive = string | number | boolean | null | bigint | symbol | undefined;
12
+ ```
13
+
14
+ ```ts
15
+ import type { Primitive } from 'ts-gems';
16
+
17
+ function serialize(value: Primitive): string {
18
+ return String(value);
19
+ }
20
+
21
+ serialize('x'); // ok
22
+ serialize({}); // type error - object is not a Primitive
23
+ ```
24
+
25
+ ## `Builtin`
26
+
27
+ ```ts
28
+ type Builtin =
29
+ | Primitive
30
+ | Function
31
+ | String
32
+ | Number
33
+ | Date
34
+ | Error
35
+ | RegExp
36
+ | Buffer
37
+ | ArrayBuffer
38
+ | Int8Array
39
+ | Uint8Array
40
+ | Uint8ClampedArray
41
+ | Int16Array
42
+ | Uint16Array
43
+ | Int32Array
44
+ | Uint32Array
45
+ | Float32Array
46
+ | Float64Array
47
+ | URL
48
+ | ReadableStream
49
+ | WritableStream;
50
+ ```
51
+
52
+ The set of JavaScript built-in types that the `Deep*`/`Deeper*` transform
53
+ families (see [the root convention](../api.md#the-deep--deeper-convention))
54
+ treat as **leaves** — never recursed into. Note that `Map`/`Set`/`WeakMap`/
55
+ `WeakSet` are _not_ part of `Builtin`; those are excluded from deep
56
+ processing separately (see [`IfNoDeepValue`](helpers.md#ifnodeepvaluet)).
57
+
58
+ ## `Type<T = any>`
59
+
60
+ ```ts
61
+ interface Type<T = any> {
62
+ new (...args: any[]): T;
63
+ }
64
+ ```
65
+
66
+ Represents _a constructor_ whose instances are of type `T` — i.e. the value
67
+ `SomeClass` itself (`typeof SomeClass`), not an instance of it.
68
+
69
+ ```ts
70
+ import type { Type } from 'ts-gems';
71
+
72
+ class User {
73
+ constructor(public name: string) {}
74
+ }
75
+
76
+ function createInstance<T>(ctor: Type<T>, ...args: any[]): T {
77
+ return new ctor(...args);
78
+ }
79
+
80
+ const user = createInstance(User, 'Ada'); // user: User
81
+ ```
82
+
83
+ ## `Class<Args, Instance, Static>`
84
+
85
+ ```ts
86
+ type Class<Args extends any[] = any[], Instance = {}, Static = {}> = (new (
87
+ ...args: Args
88
+ ) => Instance) &
89
+ Static;
90
+ ```
91
+
92
+ Like [`Type`](#typet--any), but additionally lets you describe the
93
+ constructor's own (static) members and the exact constructor argument types.
94
+
95
+ ```ts
96
+ import type { Class } from 'ts-gems';
97
+
98
+ type UserClass = Class<[name: string], { name: string }, { table: string }>;
99
+
100
+ declare const UserModel: UserClass;
101
+ UserModel.table; // string - a static member
102
+ new UserModel('Ada').name; // string - an instance member
103
+ ```
104
+
105
+ ## `Maybe<T>`
106
+
107
+ ```ts
108
+ type Maybe<T> = T | undefined;
109
+ ```
110
+
111
+ ```ts
112
+ import type { Maybe } from 'ts-gems';
113
+
114
+ function greet(name: Maybe<string>) {
115
+ return `Hello, ${name ?? 'stranger'}`;
116
+ }
117
+ ```
118
+
119
+ ## `Nullish<T = null>`
120
+
121
+ ```ts
122
+ type Nullish<T = null> = T | undefined | null;
123
+ ```
124
+
125
+ ```ts
126
+ import type { Nullish } from 'ts-gems';
127
+
128
+ let value: Nullish<number>; // number | undefined | null
129
+ ```
130
+
131
+ ## `Awaited<T>`
132
+
133
+ ```ts
134
+ type Awaited<T> = T extends PromiseLike<infer U> ? U : T;
135
+ ```
136
+
137
+ Unwraps a `Promise`/`PromiseLike`, or returns `T` unchanged if it isn't one.
138
+
139
+ ```ts
140
+ import type { Awaited } from 'ts-gems';
141
+
142
+ type A = Awaited<Promise<string>>; // string
143
+ type B = Awaited<number>; // number
144
+ ```
145
+
146
+ ## `Thunk<T>` / `ThunkAsync<T>`
147
+
148
+ ```ts
149
+ type Thunk<T> = T | (() => T);
150
+ type ThunkAsync<T> = Thunk<T> | (() => Promise<T>);
151
+ ```
152
+
153
+ A value, or a (possibly async) function that produces one — useful for
154
+ lazily-computed configuration values.
155
+
156
+ ```ts
157
+ import type { Thunk } from 'ts-gems';
158
+
159
+ function resolveThunk<T>(thunk: Thunk<T>): T {
160
+ return typeof thunk === 'function' ? (thunk as () => T)() : thunk;
161
+ }
162
+
163
+ resolveThunk(42); // ok
164
+ resolveThunk(() => 42); // ok
165
+ ```
166
+
167
+ ## `TypeThunk<T = any>` / `TypeThunkAsync<T = any>`
168
+
169
+ ```ts
170
+ type TypeThunk<T = any> = Thunk<Type<T>>;
171
+ type TypeThunkAsync<T = any> = ThunkAsync<Type<T>>;
172
+ ```
173
+
174
+ A [`Type<T>`](#typet--any) (a class reference), or a function that returns
175
+ one — a common pattern for deferring circular class references (e.g. in
176
+ decorator-based ORMs/DI containers).
177
+
178
+ ```ts
179
+ import type { TypeThunk } from 'ts-gems';
180
+
181
+ class Author {}
182
+
183
+ const authorType: TypeThunk<Author> = () => Author;
184
+ ```
185
+
186
+ ## `MaybePromise<T>`
187
+
188
+ ```ts
189
+ type MaybePromise<T> = T | Promise<T>;
190
+ ```
191
+
192
+ ```ts
193
+ import type { MaybePromise } from 'ts-gems';
194
+
195
+ async function run(fn: () => MaybePromise<number>): Promise<number> {
196
+ return fn();
197
+ }
198
+ ```
199
+
200
+ ## `PropertyType<T, K>`
201
+
202
+ ```ts
203
+ type PropertyType<T, K extends keyof T> = T[K];
204
+ ```
205
+
206
+ An explicit, named alias for indexed access — mostly useful for documentation
207
+ clarity or as a stable API when refactoring generic helpers.
208
+
209
+ ```ts
210
+ import type { PropertyType } from 'ts-gems';
211
+
212
+ interface User {
213
+ name: string;
214
+ }
215
+
216
+ type NameType = PropertyType<User, 'name'>; // string
217
+ ```
218
+
219
+ ## `ElementType<T, K>`
220
+
221
+ ```ts
222
+ type ElementType<
223
+ T extends { [P in K & any]: any },
224
+ K extends keyof T | number,
225
+ > = T[K];
226
+ ```
227
+
228
+ Returns the element type of an array, tuple, or object at index/key `K`.
229
+
230
+ ```ts
231
+ import type { ElementType } from 'ts-gems';
232
+
233
+ type Tuple = [string, number];
234
+ type First = ElementType<Tuple, 0>; // string
235
+
236
+ type List = string[];
237
+ type Item = ElementType<List, number>; // string
238
+ ```
package/docs/api.md ADDED
@@ -0,0 +1,113 @@
1
+ <!--
2
+ docs-baseline
3
+ git-commit: bfe8777ee92aea1e8cacecfa8f68e297d4235c19
4
+ package-version: 4.0.0
5
+ date: 2026-09-09
6
+ verified-against: lib/
7
+ diff-command: git diff bfe8777ee92aea1e8cacecfa8f68e297d4235c19..HEAD -- lib/
8
+ -->
9
+
10
+ <p align="center">
11
+ <img src="logo.svg" alt="ts-gems logo" width="200" height="200" />
12
+ </p>
13
+
14
+ # API Documentation
15
+
16
+ **ts-gems** is a pure TypeScript type-declaration library (no runtime code except
17
+ twelve small `as*` cast helpers). It ships utility types for transforming,
18
+ narrowing, and inspecting other types.
19
+
20
+ ```bash
21
+ npm install ts-gems --save
22
+ ```
23
+
24
+ ```ts
25
+ import { DeepPartial, DTO, StrictOmit } from 'ts-gems';
26
+ ```
27
+
28
+ Every exported type is re-exported from the package root (`ts-gems`), so you
29
+ never need to import from a sub-path. The pages below group the exports the
30
+ same way the source does, one page per module.
31
+
32
+ ## Pages
33
+
34
+ | Page | Contents |
35
+ | -------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
36
+ | [Type Guards](api/type-check.md) | `IfAny`, `IfNever`, `IfEquals`, `IfCompatible`, `IfTuple`, `IfClass`, `IfObject`, and 15+ more `If*` predicates |
37
+ | [Base Types](api/types.md) | `Type`, `Class`, `Primitive`, `Builtin`, `Maybe`, `Nullish`, `Awaited`, `ElementType`, ... |
38
+ | [Mutable](api/mutable.md) | `Mutable`, `DeepMutable`, `DeeperMutable`, `MutableSome`, `PickMutable`, `OmitMutable`, ... |
39
+ | [Readonly](api/readonly.md) | `DeepReadonly`, `DeeperReadonly`, `ReadonlySome`, `PickReadonly`, `OmitReadonly`, ... |
40
+ | [Partial](api/partial.md) | `DeepPartial`, `DeeperPartial`, `PartialSome`, `PickOptional`, `OmitOptional`, ... |
41
+ | [Required](api/required.md) | `DeepRequired`, `DeeperRequired`, `RequiredSome`, `PickRequired`, `OmitRequired`, ... |
42
+ | [Pick](api/pick.md) | `StrictPick`, `PickFunctions`, `PickTypes`, `StrictPickTypes`, `FunctionKeys`, ... |
43
+ | [Omit](api/omit.md) | `StrictOmit`, `OmitFunctions`, `OmitTypes`, `DeepOmitTypes`, `DeeperOmitTypes` |
44
+ | [OmitNever](api/omit-never.md) | `OmitNever`, `DeepOmitNever`, `DeeperOmitNever` |
45
+ | [OmitUndefined](api/omit-undefined.md) | `OmitUndefined`, `DeepOmitUndefined`, `DeeperOmitUndefined` |
46
+ | [Nullish](api/nullish.md) | `NullishObject`, `DeepNullish`, `DeeperNullish` |
47
+ | [UnNullish](api/non-nullable.md) | `UnNullish`, `DeepUnNullish`, `DeeperUnNullish` |
48
+ | [DTO](api/dto.md) | `DTO`, `PartialDTO`, `PatchDTO` |
49
+ | [Combine](api/combine.md) | `Combine` |
50
+ | [Logical](api/logical.md) | `And`, `Or` |
51
+ | [Opaque](api/opaque.md) | `Opaque` |
52
+ | [Helpers](api/helpers.md) | `IfNoDeepValue`, `ValuesOf` |
53
+
54
+ ## The `Deep*` / `Deeper*` convention
55
+
56
+ Several families in this library (`Mutable`, `Readonly`, `Partial`, `Required`,
57
+ `Nullish`, `UnNullish`, `OmitNever`, `OmitUndefined`, `OmitTypes`, `DTO`) come
58
+ in three strengths. Read this once — every page below assumes it:
59
+
60
+ | Variant | Recurses into nested objects? | Recurses into array elements? |
61
+ | --------- | ----------------------------- | --------------------------------------------- |
62
+ | _(base)_ | No — shallow, top level only | No |
63
+ | `Deep*` | Yes | No — array-typed properties are left as-is |
64
+ | `Deeper*` | Yes | Yes — array element types are transformed too |
65
+
66
+ **Leaf types are never recursed into**, by any variant, including `Deeper*`.
67
+ A property is treated as a leaf (left untouched) when its type is:
68
+
69
+ - a primitive (`string`, `number`, `boolean`, `bigint`, `symbol`, `null`, `undefined`)
70
+ - `any`
71
+ - a `Function` or a constructor/class reference (e.g. `typeof SomeClass`, `Type<T>`)
72
+ - `Map`, `ReadonlyMap`, `WeakMap`, `Set`, `ReadonlySet`, or `WeakSet`
73
+ - a **tuple** (`[string, number]`, `[]`, etc.) — a fixed-length array is left
74
+ exactly as-is, even by `Deeper*` variants. Only _variable-length_ arrays
75
+ (`T[]` or `readonly T[]`) have their element type transformed.
76
+ - a `Date`, `RegExp`, or any other `Builtin` (see [Base Types](api/types.md#builtin))
77
+
78
+ Both mutable (`T[]`) and readonly (`readonly T[]`) variable-length arrays are
79
+ recognized the same way. A `Deeper*` variant that transforms a `readonly T[]`
80
+ property always rebuilds it as a plain `T[]` — only the _element_ type's
81
+ readonly-ness (or lack of it) is guaranteed by the transform, not the
82
+ wrapper's; see the note on [`DeeperReadonly`](api/readonly.md#deeperreadonlyt).
83
+
84
+ ```ts
85
+ import type { DeeperReadonly } from 'ts-gems';
86
+
87
+ type Config = {
88
+ name: string;
89
+ address: { city: string }[]; // plain array -> element type IS transformed
90
+ range: [number, number]; // tuple -> left untouched, even by Deeper*
91
+ };
92
+
93
+ type ReadonlyConfig = DeeperReadonly<Config>;
94
+ // {
95
+ // readonly name: string;
96
+ // readonly address: { readonly city: string }[]; // element type made readonly
97
+ // readonly range: [number, number]; // unchanged shape
98
+ // }
99
+ ```
100
+
101
+ ## Naming patterns used across pages
102
+
103
+ - **`Pick*` / `Omit*`** — select or remove properties matching some criterion
104
+ (readonly-ness, required-ness, function-ness, type match, ...).
105
+ - **`Strict*`** — a stricter variant of a `Pick`/`Omit` type that also excludes
106
+ `unknown`, `any`, and `{}` from matching, and requires an exact key
107
+ (`X extends keyof T`) rather than an arbitrary type.
108
+ - **`*Some<T, K>`** — apply a transform to only the properties named in `K`,
109
+ leaving the rest of `T` untouched (e.g. `MutableSome<T, 'a' | 'b'>`).
110
+ - **`*Keys<T>`** — returns the union of keys matching some criterion
111
+ (`keyof Pick*<T>`, purely for convenience).
112
+
113
+ See each page for full signatures, descriptions and runnable examples.
package/docs/logo.svg ADDED
@@ -0,0 +1,43 @@
1
+ <svg version="1.1" id="fi_919832" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;">
2
+ <defs></defs>
3
+ <g id="fi_2546715" transform="matrix(0.999975, 0, 0, 0.999975, -188.837326, 635.496765)" style="">
4
+ <g transform="matrix(1, 0, 0, 1, 189.628876, -636.415588)">
5
+ <path d="m492.561 158.013c-25.95-62.577-75.997-112.625-138.574-138.574l-157.987 236.561 157.987 236.561c62.577-25.949 112.625-75.997 138.574-138.574z" fill="#ba1956"></path>
6
+ <g fill="#ff0f47">
7
+ <path d="m492.561 158.013-236.561 97.987 236.561 97.987c12.517-30.185 19.439-63.276 19.439-97.987 0-34.712-6.922-67.802-19.439-97.987z"></path>
8
+ <path d="m353.987 19.439c-30.185-12.517-63.275-19.439-97.987-19.439l-60 256 60 256c34.712 0 67.802-6.922 97.987-19.439l-97.987-236.561z"></path>
9
+ <path d=""></path>
10
+ <path d="m158.013 19.439c-62.576 25.95-112.624 75.998-138.574 138.574v195.973c25.949 62.577 75.997 112.625 138.574 138.574h77.987l20-236.56-20-236.561z"></path>
11
+ </g>
12
+ <path d="m158.013 492.561c30.185 12.517 63.276 19.439 97.987 19.439v-256z" fill="#ff415d"></path>
13
+ <path d="m256 0c-34.711 0-67.802 6.922-97.987 19.439l97.987 236.561z" fill="#ff415d"></path>
14
+ <path d="m19.439 158.013c-12.517 30.185-19.439 63.275-19.439 97.987 0 34.711 6.922 67.802 19.439 97.987l236.561-97.987z" fill="#ff415d"></path>
15
+ <path d="m312.333 120h-56.333l-60 136 60 136h56.333l79.667-79.667v-112.666z" fill="#ff415d"></path>
16
+ <path d="m199.667 120-79.667 79.667v112.666l79.667 79.667h56.333v-272z" fill="#ff7472"></path>
17
+ </g>
18
+ </g>
19
+ <g></g>
20
+ <g></g>
21
+ <g></g>
22
+ <g></g>
23
+ <g></g>
24
+ <g></g>
25
+ <g></g>
26
+ <g></g>
27
+ <g></g>
28
+ <g></g>
29
+ <g></g>
30
+ <g></g>
31
+ <g></g>
32
+ <g></g>
33
+ <g></g>
34
+ <g>
35
+ <path style="fill:#F2F2F2;" d="M503.16,322.936c-13.479,49.894-41.66,93.748-79.621,126.631c-3.709,0.199-7.586,0.293-11.609,0.293
36
+ c-53.154,0-82.693-33.217-97.374-58.138l48.191-28.954c0,0,16.394,34.837,47.125,34.837c30.741,0,43.039-10.25,43.039-33.813
37
+ c0-28.693-99.391-38.933-114.761-88.116c-15.37-49.183,5.12-118.868,76.852-113.737c44.826,3.197,70.04,25.213,82.651,41.273
38
+ l-47.815,34.544c-10.25-29.706-52.255-29.706-60.458-5.12c-8.192,24.597,19.466,38.933,62.506,53.279
39
+ C473.997,293.282,491.781,306.876,503.16,322.936z"></path>
40
+ <polygon style="fill:#F2F2F2;" points="300.943,169.786 83.106,169.786 83.106,221.202 162.935,221.202 162.935,444.45
41
+ 221.115,444.45 221.115,221.202 300.943,221.202 "></polygon>
42
+ </g>
43
+ </svg>
package/lib/dto.d.ts CHANGED
@@ -1,24 +1,26 @@
1
1
  import { IfNoDeepValue } from './helpers.js';
2
2
  import { DeeperNullish } from './nullish.js';
3
3
  import { DeeperPartial } from './partial.js';
4
- import { IfNever } from './type-check.js';
4
+ import { IfNever, IfTuple } from './type-check.js';
5
5
 
6
6
  /**
7
7
  * Returns the given type as a Data Transfer Object (DTO) interface, Removes symbol keys and function properties.
8
8
  * @template T - The type of the data being transferred.
9
9
  */
10
10
  export type DTO<T, X = never> = {
11
- [K in keyof T as IfNever<
12
- Exclude<NonNullable<T[K]>, Function | symbol>,
13
- never,
14
- K
15
- >]: NonNullable<T[K]> extends (infer U)[] // Deep process arrays
16
- ? DTO<U>[]
17
- : // Do not deep process No-Deep values
18
- IfNoDeepValue<NonNullable<T[K]>> extends true
19
- ? NonNullable<T[K] | X>
20
- : // Deep process objects
21
- DTO<NonNullable<T[K] | X>>;
11
+ [
12
+ K in keyof T as K extends symbol
13
+ ? never
14
+ : IfNever<Exclude<NonNullable<T[K]>, Function | symbol>, never, K>
15
+ ]: IfTuple<NonNullable<T[K]>> extends true // Leave fixed-length tuples untouched
16
+ ? NonNullable<T[K] | X>
17
+ : NonNullable<T[K]> extends readonly (infer U)[] // Deep process arrays
18
+ ? DTO<U>[]
19
+ : // Do not deep process No-Deep values
20
+ IfNoDeepValue<NonNullable<T[K]>> extends true
21
+ ? NonNullable<T[K] | X>
22
+ : // Deep process objects
23
+ DTO<NonNullable<T[K] | X>>;
22
24
  };
23
25
 
24
26
  export type PartialDTO<T, X = never> = DeeperPartial<DTO<T, X>>;
package/lib/helpers.d.ts CHANGED
@@ -4,31 +4,32 @@ import { Builtin } from './types.js';
4
4
  /**
5
5
  * Returns true if T is excluded from deep operations
6
6
  */
7
- export type IfNoDeepValue<T> = T extends Builtin
8
- ? true
9
- : IfAny<T> extends true
10
- ? T
11
- : IfTuple<T> extends true
7
+ export type IfNoDeepValue<T> =
8
+ IfAny<T> extends true
9
+ ? true
10
+ : T extends Builtin
12
11
  ? true
13
- : T extends Function
12
+ : IfTuple<T> extends true
14
13
  ? true
15
- : IfClass<T> extends true
14
+ : T extends Function
16
15
  ? true
17
- : T extends Map<any, any>
16
+ : IfClass<T> extends true
18
17
  ? true
19
- : T extends ReadonlyMap<any, any>
18
+ : T extends Map<any, any>
20
19
  ? true
21
- : T extends WeakMap<any, any>
20
+ : T extends ReadonlyMap<any, any>
22
21
  ? true
23
- : T extends Set<any>
22
+ : T extends WeakMap<any, any>
24
23
  ? true
25
- : T extends ReadonlySet<any>
24
+ : T extends Set<any>
26
25
  ? true
27
- : T extends WeakSet<any>
26
+ : T extends ReadonlySet<any>
28
27
  ? true
29
- : T extends any[]
28
+ : T extends WeakSet<any>
30
29
  ? true
31
- : false;
30
+ : T extends readonly any[]
31
+ ? true
32
+ : false;
32
33
 
33
34
  /**
34
35
  * ValuesOf
package/lib/mutable.d.ts CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  OmitReadonly,
8
8
  PickReadonly,
9
9
  } from './readonly.js';
10
- import { IfNever } from './type-check.js';
10
+ import { IfNever, IfTuple } from './type-check.js';
11
11
 
12
12
  /**
13
13
  * Make all properties in T mutable
@@ -26,11 +26,9 @@ export type MutableSome<T, K extends keyof T> = Mutable<Pick<T, K>> &
26
26
  * Make all properties in T mutable deeply
27
27
  */
28
28
  export type DeepMutable<T> = {
29
- -readonly [K in keyof T as IfNever<
30
- Exclude<T[K], undefined>,
31
- never,
32
- K
33
- >]: IfNoDeepValue<Exclude<T[K], undefined>> extends true // Do not deep process No-Deep values
29
+ -readonly [
30
+ K in keyof T as IfNever<Exclude<T[K], undefined>, never, K>
31
+ ]: IfNoDeepValue<Exclude<T[K], undefined>> extends true // Do not deep process No-Deep values
34
32
  ? T[K]
35
33
  : // Deep process objects
36
34
  DeepMutable<NonNullable<T[K]>>;
@@ -40,17 +38,17 @@ export type DeepMutable<T> = {
40
38
  * Make all properties in T mutable deeply
41
39
  */
42
40
  export type DeeperMutable<T> = {
43
- -readonly [K in keyof T as IfNever<
44
- Exclude<T[K], undefined>,
45
- never,
46
- K
47
- >]: NonNullable<T[K]> extends (infer U)[] // Deep process arrays
48
- ? DeeperMutable<U>[]
49
- : // Do not deep process No-Deep values
50
- IfNoDeepValue<NonNullable<T[K]>> extends true
51
- ? T[K]
52
- : // Deep process objects
53
- DeeperMutable<NonNullable<T[K]>>;
41
+ -readonly [
42
+ K in keyof T as IfNever<Exclude<T[K], undefined>, never, K>
43
+ ]: IfTuple<NonNullable<T[K]>> extends true // Leave fixed-length tuples untouched
44
+ ? T[K]
45
+ : NonNullable<T[K]> extends readonly (infer U)[] // Deep process arrays
46
+ ? DeeperMutable<U>[]
47
+ : // Do not deep process No-Deep values
48
+ IfNoDeepValue<NonNullable<T[K]>> extends true
49
+ ? T[K]
50
+ : // Deep process objects
51
+ DeeperMutable<NonNullable<T[K]>>;
54
52
  };
55
53
 
56
54
  /**
@@ -1,26 +1,30 @@
1
1
  import { IfNoDeepValue } from './helpers.js';
2
- import { IfNever, IfNull } from './type-check.js';
2
+ import { IfNever, IfNull, IfTuple } from './type-check.js';
3
3
 
4
4
  /**
5
- * Exclude null and undefined from T deeply
5
+ * Exclude null and undefined from T
6
6
  */
7
7
  export type UnNullish<T> = {
8
- [K in keyof T as IfNever<
9
- Exclude<T[K], undefined>,
10
- never,
11
- IfNull<Exclude<T[K], undefined>, never, K>
12
- >]: NonNullable<T[K]>;
8
+ [
9
+ K in keyof T as IfNever<
10
+ Exclude<T[K], undefined>,
11
+ never,
12
+ IfNull<Exclude<T[K], undefined>, never, K>
13
+ >
14
+ ]: NonNullable<T[K]>;
13
15
  };
14
16
 
15
17
  /**
16
18
  * Exclude null and undefined from T deeply
17
19
  */
18
20
  export type DeepUnNullish<T> = {
19
- [K in keyof T as IfNever<
20
- Exclude<T[K], undefined>,
21
- never,
22
- IfNull<Exclude<T[K], undefined>, never, K>
23
- >]: IfNoDeepValue<
21
+ [
22
+ K in keyof T as IfNever<
23
+ Exclude<T[K], undefined>,
24
+ never,
25
+ IfNull<Exclude<T[K], undefined>, never, K>
26
+ >
27
+ ]: IfNoDeepValue<
24
28
  // Do not deep process No-Deep values
25
29
  Exclude<T[K], undefined>
26
30
  > extends true
@@ -33,15 +37,19 @@ export type DeepUnNullish<T> = {
33
37
  * Exclude null and undefined from T deeply including arrays
34
38
  */
35
39
  export type DeeperUnNullish<T> = {
36
- [K in keyof T as IfNever<
37
- Exclude<T[K], undefined>,
38
- never,
39
- IfNull<Exclude<T[K], undefined>, never, K>
40
- >]: NonNullable<NonNullable<T[K]>> extends (infer U)[]
41
- ? DeeperUnNullish<U>[]
42
- : // Do not deep process No-Deep values
43
- IfNoDeepValue<NonNullable<T[K]>> extends true
44
- ? NonNullable<T[K]>
45
- : // Deep process objects
46
- DeeperUnNullish<NonNullable<T[K]>>;
40
+ [
41
+ K in keyof T as IfNever<
42
+ Exclude<T[K], undefined>,
43
+ never,
44
+ IfNull<Exclude<T[K], undefined>, never, K>
45
+ >
46
+ ]: IfTuple<NonNullable<T[K]>> extends true // Leave fixed-length tuples untouched
47
+ ? NonNullable<T[K]>
48
+ : NonNullable<NonNullable<T[K]>> extends readonly (infer U)[]
49
+ ? DeeperUnNullish<U>[]
50
+ : // Do not deep process No-Deep values
51
+ IfNoDeepValue<NonNullable<T[K]>> extends true
52
+ ? NonNullable<T[K]>
53
+ : // Deep process objects
54
+ DeeperUnNullish<NonNullable<T[K]>>;
47
55
  };
package/lib/nullish.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { IfNoDeepValue } from './helpers.js';
2
- import { IfNever } from './type-check.js';
2
+ import { IfNever, IfTuple } from './type-check.js';
3
3
 
4
4
  /**
5
5
  * Make all properties in T nullish
@@ -25,14 +25,18 @@ export type DeepNullish<T> = {
25
25
  * Make all properties in T nullish deeply including arrays
26
26
  */
27
27
  export type DeeperNullish<T> = {
28
- [K in keyof T as IfNever<Exclude<T[K], undefined>, never, K>]?: NonNullable<
29
- // Deep process arrays
30
- T[K]
31
- > extends (infer U)[]
32
- ? DeeperNullish<U>[] | null
33
- : // Do not deep process No-Deep values
34
- IfNoDeepValue<NonNullable<T[K]>> extends true
35
- ? T[K] | null
36
- : // Deep process objects
37
- DeeperNullish<NonNullable<T[K]>> | null;
28
+ [K in keyof T as IfNever<Exclude<T[K], undefined>, never, K>]?: IfTuple<
29
+ NonNullable<T[K]>
30
+ > extends true // Leave fixed-length tuples untouched
31
+ ? T[K] | null
32
+ : NonNullable<
33
+ // Deep process arrays
34
+ T[K]
35
+ > extends readonly (infer U)[]
36
+ ? DeeperNullish<U>[] | null
37
+ : // Do not deep process No-Deep values
38
+ IfNoDeepValue<NonNullable<T[K]>> extends true
39
+ ? T[K] | null
40
+ : // Deep process objects
41
+ DeeperNullish<NonNullable<T[K]>> | null;
38
42
  };