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,66 @@
1
+ # Nullish
2
+
3
+ Source: [`lib/nullish.d.ts`](../../lib/nullish.d.ts)
4
+
5
+ Makes properties optional **and** nullable (`T[K] | null`) — useful for
6
+ "patch"-style inputs where a client can send `null` to explicitly clear a
7
+ field, as opposed to omitting the key to leave it unchanged. See [the
8
+ `Deep*`/`Deeper*` convention](../api.md#the-deep--deeper-convention) and
9
+ [`PatchDTO`](dto.md#patchdtot-x--never) which builds on `DeeperNullish`.
10
+
11
+ ## `NullishObject<T>`
12
+
13
+ ```ts
14
+ import type { NullishObject } from 'ts-gems';
15
+
16
+ interface User {
17
+ name: string;
18
+ age: number;
19
+ }
20
+
21
+ type PatchUser = NullishObject<User>;
22
+ // { name?: string | null; age?: number | null }
23
+ ```
24
+
25
+ ## `DeepNullish<T>`
26
+
27
+ Like `NullishObject`, but also makes nested object properties optional and
28
+ nullable, recursively. Array-typed properties are left as-is (only wrapped
29
+ in `| null` at their own level).
30
+
31
+ ```ts
32
+ import type { DeepNullish } from 'ts-gems';
33
+
34
+ interface Config {
35
+ name: string;
36
+ server: { host: string; port: number };
37
+ tags: string[];
38
+ }
39
+
40
+ type PatchConfig = DeepNullish<Config>;
41
+ // {
42
+ // name?: string | null;
43
+ // server?: { host?: string | null; port?: number | null } | null;
44
+ // tags?: string[] | null;
45
+ // }
46
+ ```
47
+
48
+ ## `DeeperNullish<T>`
49
+
50
+ Like `DeepNullish`, but also recurses into array elements. Tuples are
51
+ preserved as-is (only wrapped in `| null`).
52
+
53
+ ```ts
54
+ import type { DeeperNullish } from 'ts-gems';
55
+
56
+ interface Config {
57
+ servers: { host: string }[];
58
+ range: [number, number]; // tuple
59
+ }
60
+
61
+ type PatchConfig = DeeperNullish<Config>;
62
+ // {
63
+ // servers?: ({ host?: string | null }[]) | null;
64
+ // range?: [number, number] | null; // tuple - unchanged, just nullable
65
+ // }
66
+ ```
@@ -0,0 +1,62 @@
1
+ # OmitNever
2
+
3
+ Source: [`lib/omit-never.d.ts`](../../lib/omit-never.d.ts)
4
+
5
+ Removes properties whose value type is `never` — handy after a chain of
6
+ conditional-type transforms leaves some properties resolved to `never`
7
+ (which should behave like "this key doesn't exist"). See [the `Deep*`/
8
+ `Deeper*` convention](../api.md#the-deep--deeper-convention).
9
+
10
+ ## `OmitNever<T>`
11
+
12
+ ```ts
13
+ import type { OmitNever } from 'ts-gems';
14
+
15
+ type MyType = {
16
+ a: string;
17
+ b: number;
18
+ c?: never;
19
+ };
20
+
21
+ type Result = OmitNever<MyType>;
22
+ // { a: string; b: number }
23
+ ```
24
+
25
+ ## `DeepOmitNever<T>`
26
+
27
+ Like `OmitNever`, but also applies the same removal recursively to nested
28
+ object properties. Array-typed properties are left as-is.
29
+
30
+ ```ts
31
+ import type { DeepOmitNever } from 'ts-gems';
32
+
33
+ type MyType = {
34
+ a: string;
35
+ nested: { b: number; c: never };
36
+ list: { b: number; c: never }[];
37
+ };
38
+
39
+ type Result = DeepOmitNever<MyType>;
40
+ // {
41
+ // a: string;
42
+ // nested: { b: number };
43
+ // list: { b: number; c: never }[]; // untouched - it's an array
44
+ // }
45
+ ```
46
+
47
+ ## `DeeperOmitNever<T>`
48
+
49
+ Like `DeepOmitNever`, but also recurses into array elements. Tuples are
50
+ preserved as-is.
51
+
52
+ ```ts
53
+ import type { DeeperOmitNever } from 'ts-gems';
54
+
55
+ type MyType = {
56
+ list: { b: number; c: never }[];
57
+ pair: [string, never];
58
+ };
59
+
60
+ type Result = DeeperOmitNever<MyType>;
61
+ // { list: { b: number }[]; pair: [string, never] } - tuple untouched
62
+ ```
@@ -0,0 +1,60 @@
1
+ # OmitUndefined
2
+
3
+ Source: [`lib/omit-undefined.d.ts`](../../lib/omit-undefined.d.ts)
4
+
5
+ Removes properties whose value type is exactly `undefined` (properties
6
+ typed `T | undefined`, i.e. optional-looking-but-not-`?`, are narrowed
7
+ rather than removed unless the whole type collapses to `never`). See
8
+ [the `Deep*`/`Deeper*` convention](../api.md#the-deep--deeper-convention).
9
+
10
+ ## `OmitUndefined<T>`
11
+
12
+ ```ts
13
+ import type { OmitUndefined } from 'ts-gems';
14
+
15
+ type MyType = {
16
+ a?: number;
17
+ b: string;
18
+ c: undefined;
19
+ };
20
+
21
+ type Result = OmitUndefined<MyType>;
22
+ // { a?: number; b: string } - `c` (exactly `undefined`) is dropped
23
+ ```
24
+
25
+ ## `DeepOmitUndefined<T>`
26
+
27
+ Like `OmitUndefined`, but also applies the same removal recursively to
28
+ nested object properties. Array-typed properties are left as-is.
29
+
30
+ ```ts
31
+ import type { DeepOmitUndefined } from 'ts-gems';
32
+
33
+ type MyType = {
34
+ nested: { a?: string; b: undefined };
35
+ list: { a?: string; b: undefined }[];
36
+ };
37
+
38
+ type Result = DeepOmitUndefined<MyType>;
39
+ // {
40
+ // nested: { a?: string };
41
+ // list: { a?: string; b: undefined }[]; // untouched - it's an array
42
+ // }
43
+ ```
44
+
45
+ ## `DeeperOmitUndefined<T>`
46
+
47
+ Like `DeepOmitUndefined`, but also recurses into array elements. Tuples are
48
+ preserved as-is.
49
+
50
+ ```ts
51
+ import type { DeeperOmitUndefined } from 'ts-gems';
52
+
53
+ type MyType = {
54
+ list: { a?: string; b: undefined }[];
55
+ pair: [string, undefined];
56
+ };
57
+
58
+ type Result = DeeperOmitUndefined<MyType>;
59
+ // { list: { a?: string }[]; pair: [string, undefined] } - tuple untouched
60
+ ```
@@ -0,0 +1,99 @@
1
+ # Omit
2
+
3
+ Source: [`lib/omit.d.ts`](../../lib/omit.d.ts)
4
+
5
+ Removing properties by key, by kind (function vs. data), or by value type.
6
+ See [Pick](pick.md) for the inverse operations, and [the `Deep*`/`Deeper*`
7
+ convention](../api.md#the-deep--deeper-convention) for `DeepOmitTypes`/
8
+ `DeeperOmitTypes`.
9
+
10
+ ## `StrictOmit<T, X>`
11
+
12
+ Like the built-in `Omit<T, K>`, but also drops any remaining key whose value
13
+ type is `never` (after stripping `undefined`) — symmetric with
14
+ [`StrictPick`](pick.md#strictpickt-x), so picking and omitting the same key
15
+ set never disagree about `never`-typed properties.
16
+
17
+ ```ts
18
+ import type { StrictOmit } from 'ts-gems';
19
+
20
+ interface Row {
21
+ a?: number;
22
+ b: string;
23
+ c: never;
24
+ }
25
+
26
+ type Result = StrictOmit<Row, 'b'>;
27
+ // { a?: number } - `c` is dropped too, it's `never`
28
+ ```
29
+
30
+ ## `OmitFunctions<T>`
31
+
32
+ Removes every property whose value is a function — the inverse of
33
+ [`PickFunctions`](pick.md#pickfunctionst).
34
+
35
+ ```ts
36
+ import type { OmitFunctions } from 'ts-gems';
37
+
38
+ interface Service {
39
+ name: string;
40
+ start(): void;
41
+ }
42
+
43
+ type Data = OmitFunctions<Service>;
44
+ // { name: string }
45
+ ```
46
+
47
+ ## `OmitTypes<T, X>`
48
+
49
+ Removes properties whose value type is assignable to or from `X`, and
50
+ narrows the remaining properties' types by excluding `X` from their union
51
+ (via `Exclude<T[K], X>`) — the inverse of
52
+ [`PickTypes`](pick.md#picktypest-x).
53
+
54
+ ```ts
55
+ import type { OmitTypes } from 'ts-gems';
56
+
57
+ interface Row {
58
+ a: number;
59
+ b: boolean;
60
+ c: string | number | boolean;
61
+ }
62
+
63
+ type Result = OmitTypes<Row, number>;
64
+ // { b: boolean; c: string | boolean } - `a` is dropped, `number` removed from `c`
65
+ ```
66
+
67
+ ## `DeepOmitTypes<T, X>`
68
+
69
+ Like `OmitTypes`, but also applies the same removal recursively to nested
70
+ object properties. Array-typed properties are left as-is.
71
+
72
+ ```ts
73
+ import type { DeepOmitTypes } from 'ts-gems';
74
+
75
+ interface Row {
76
+ a: number;
77
+ nested: { a: number; b: string };
78
+ }
79
+
80
+ type Result = DeepOmitTypes<Row, number>;
81
+ // { nested: { b: string } }
82
+ ```
83
+
84
+ ## `DeeperOmitTypes<T, X>`
85
+
86
+ Like `DeepOmitTypes`, but also recurses into array elements. Tuples are
87
+ preserved as-is.
88
+
89
+ ```ts
90
+ import type { DeeperOmitTypes } from 'ts-gems';
91
+
92
+ interface Row {
93
+ items: { a: number; b: string }[];
94
+ pair: [string, number]; // tuple
95
+ }
96
+
97
+ type Result = DeeperOmitTypes<Row, number>;
98
+ // { items: { b: string }[]; pair: [string, number] } - tuple untouched
99
+ ```
@@ -0,0 +1,56 @@
1
+ # Opaque
2
+
3
+ Source: [`lib/opaque.d.ts`](../../lib/opaque.d.ts)
4
+
5
+ ## `Opaque<T, N extends string>`
6
+
7
+ ```ts
8
+ type Opaque<T, N extends string> = T & {
9
+ readonly [Symbols.base]: N;
10
+ readonly [Symbols.brand]: N;
11
+ };
12
+ ```
13
+
14
+ Creates a _branded_ type: structurally still based on `T`, but tagged with a
15
+ unique string literal `N` so that two `Opaque` types built from the same
16
+ base `T` are no longer interchangeable by accident. This is the classic
17
+ ["nominal typing" workaround](https://michalzalecki.com/nominal-typing-in-typescript/)
18
+ for a structurally-typed language — useful for IDs, currency amounts, or any
19
+ primitive that shouldn't be swappable with another primitive of the same
20
+ underlying type.
21
+
22
+ ```ts
23
+ import type { Opaque } from 'ts-gems';
24
+
25
+ type UserId = Opaque<number, 'UserId'>;
26
+ type ProductId = Opaque<number, 'ProductId'>;
27
+
28
+ function getUser(id: UserId) {
29
+ /* ... */
30
+ }
31
+
32
+ declare const userId: UserId;
33
+ declare const productId: ProductId;
34
+ declare const rawNumber: number;
35
+
36
+ getUser(userId); // ok
37
+ getUser(productId); // type error - different brand, even though both are `number`
38
+ getUser(rawNumber); // type error - a plain number isn't branded at all
39
+ ```
40
+
41
+ Because `Opaque<T, N>` is an intersection with `T`, a branded value is still
42
+ usable _as_ its base type — the restriction only goes one way:
43
+
44
+ ```ts
45
+ const id = 1 as UserId;
46
+ const n: number = id; // ok - a UserId structurally satisfies `number`
47
+ ```
48
+
49
+ You create a branded value with a type assertion (there's no runtime
50
+ wrapping — the brand only exists at the type level):
51
+
52
+ ```ts
53
+ function toUserId(id: number): UserId {
54
+ return id as UserId;
55
+ }
56
+ ```
@@ -0,0 +1,135 @@
1
+ # Partial
2
+
3
+ Source: [`lib/partial.d.ts`](../../lib/partial.d.ts)
4
+
5
+ Makes properties optional. See [the `Deep*`/`Deeper*`
6
+ convention](../api.md#the-deep--deeper-convention) and the runtime
7
+ [`asPartial` cast helpers](helpers.md#runtime-cast-helpers).
8
+
9
+ ## `PartialSome<T, K>`
10
+
11
+ Marks only the properties named in `K` as optional; the rest of `T` is
12
+ required exactly as declared.
13
+
14
+ ```ts
15
+ import type { PartialSome } from 'ts-gems';
16
+
17
+ interface CreateUserInput {
18
+ name: string;
19
+ email: string;
20
+ role: string;
21
+ }
22
+
23
+ type UpdateUserInput = PartialSome<CreateUserInput, 'email' | 'role'>;
24
+ // { name: string; email?: string; role?: string }
25
+ ```
26
+
27
+ ## `DeepPartial<T>`
28
+
29
+ Like the built-in `Partial<T>`, but also makes nested object properties
30
+ optional, recursively. Array-typed properties are left as-is.
31
+
32
+ ```ts
33
+ import type { DeepPartial } from 'ts-gems';
34
+
35
+ interface Config {
36
+ name: string;
37
+ server: { host: string; port: number };
38
+ tags: string[];
39
+ }
40
+
41
+ type PartialConfig = DeepPartial<Config>;
42
+ // {
43
+ // name?: string;
44
+ // server?: { host?: string; port?: number };
45
+ // tags?: string[]; // untouched - it's an array
46
+ // }
47
+ ```
48
+
49
+ ## `DeeperPartial<T>`
50
+
51
+ Like `DeepPartial`, but also makes array element properties optional.
52
+ Tuples are left untouched.
53
+
54
+ ```ts
55
+ import type { DeeperPartial } from 'ts-gems';
56
+
57
+ interface Config {
58
+ servers: { host: string }[];
59
+ range: [number, number]; // tuple
60
+ }
61
+
62
+ type PartialConfig = DeeperPartial<Config>;
63
+ // {
64
+ // servers?: { host?: string }[];
65
+ // range?: [number, number]; // tuple - unchanged
66
+ // }
67
+ ```
68
+
69
+ ## `OptionalKeys<T>`
70
+
71
+ Returns the union of property names in `T` that are optional.
72
+
73
+ ```ts
74
+ import type { OptionalKeys } from 'ts-gems';
75
+
76
+ interface Row {
77
+ id: number;
78
+ nickname?: string;
79
+ }
80
+
81
+ type Optional = OptionalKeys<Row>; // 'nickname'
82
+ ```
83
+
84
+ ## `PickOptional<T>` / `OmitOptional<T>`
85
+
86
+ Pick (or omit) only the properties that are optional. Implemented in terms
87
+ of [`OmitRequired`/`PickRequired`](required.md), since "optional" and
88
+ "required" partition every property into exactly two sets.
89
+
90
+ ```ts
91
+ import type { OmitOptional, PickOptional } from 'ts-gems';
92
+
93
+ interface Row {
94
+ id: number;
95
+ nickname?: string;
96
+ }
97
+
98
+ type OnlyOptional = PickOptional<Row>; // { nickname?: string }
99
+ type OnlyRequired = OmitOptional<Row>; // { id: number }
100
+ ```
101
+
102
+ ## `DeepPickOptional<T>` / `DeepOmitOptional<T>`
103
+
104
+ The deep versions of `PickOptional`/`OmitOptional` — nested objects are
105
+ filtered the same way, recursively. Delegates to
106
+ [`DeepOmitRequired`/`DeepPickRequired`](required.md).
107
+
108
+ ```ts
109
+ import type { DeepOmitOptional } from 'ts-gems';
110
+
111
+ interface Row {
112
+ id: number;
113
+ meta: { label?: string; note: string };
114
+ }
115
+
116
+ type Required = DeepOmitOptional<Row>;
117
+ // { id: number; meta: { note: string } }
118
+ ```
119
+
120
+ ## `DeeperPickOptional<T>` / `DeeperOmitOptional<T>`
121
+
122
+ Like `DeepPickOptional`/`DeepOmitOptional`, but also recurses into array
123
+ elements. Delegates to
124
+ [`DeeperOmitRequired`/`DeeperPickRequired`](required.md).
125
+
126
+ ```ts
127
+ import type { DeeperOmitOptional } from 'ts-gems';
128
+
129
+ interface Row {
130
+ tags: { label?: string; note: string }[];
131
+ }
132
+
133
+ type Required = DeeperOmitOptional<Row>;
134
+ // { tags: { note: string }[] }
135
+ ```
@@ -0,0 +1,115 @@
1
+ # Pick
2
+
3
+ Source: [`lib/pick.d.ts`](../../lib/pick.d.ts)
4
+
5
+ Selecting properties by key, by kind (function vs. data), or by value type.
6
+ See [Omit](omit.md) for the inverse operations.
7
+
8
+ ## `StrictPick<T, X>`
9
+
10
+ Like the built-in `Pick<T, K>`, but also drops any selected key whose value
11
+ type is `never` (after stripping `undefined`) — consistent with how the rest
12
+ of the library treats `never`-typed properties as "absent".
13
+
14
+ ```ts
15
+ import type { StrictPick } from 'ts-gems';
16
+
17
+ interface Row {
18
+ a?: number;
19
+ b: string;
20
+ c: never;
21
+ }
22
+
23
+ type Result = StrictPick<Row, 'a' | 'c'>;
24
+ // { a?: number } - `c` is dropped, it's `never`
25
+ ```
26
+
27
+ ## `PickFunctions<T>`
28
+
29
+ Keeps only the properties whose value is a function.
30
+
31
+ ```ts
32
+ import type { PickFunctions } from 'ts-gems';
33
+
34
+ interface Service {
35
+ name: string;
36
+ start(): void;
37
+ stop(): void;
38
+ }
39
+
40
+ type Methods = PickFunctions<Service>;
41
+ // { start(): void; stop(): void }
42
+ ```
43
+
44
+ ## `PickTypes<T, X>`
45
+
46
+ Keeps properties whose value type is assignable to or from `X` (a
47
+ bidirectional compatibility check, not exact equality — useful for grabbing
48
+ every property that is "roughly" of a given type, unions included).
49
+
50
+ ```ts
51
+ import type { PickTypes } from 'ts-gems';
52
+
53
+ interface Row {
54
+ a: number;
55
+ b: string;
56
+ c: string | number | boolean;
57
+ }
58
+
59
+ type NumberLike = PickTypes<Row, number>;
60
+ // { a: number; c: string | number | boolean }
61
+ ```
62
+
63
+ ## `StrictPickTypes<T, X>`
64
+
65
+ Like `PickTypes`, but additionally excludes properties typed `unknown`,
66
+ `any`, or `{}` from matching — useful when you want type-based filtering
67
+ without accidentally sweeping up untyped/loosely-typed properties.
68
+
69
+ ```ts
70
+ import type { StrictPickTypes } from 'ts-gems';
71
+
72
+ interface Row {
73
+ a: number;
74
+ b: unknown;
75
+ c: any;
76
+ }
77
+
78
+ type NumberLike = StrictPickTypes<Row, number>;
79
+ // { a: number } - `b` and `c` are excluded even though they're compatible
80
+ ```
81
+
82
+ ## `FunctionKeys<T>` / `NonFunctionKeys<T>`
83
+
84
+ Returns the union of property names whose value is (or isn't) a function.
85
+
86
+ ```ts
87
+ import type { FunctionKeys, NonFunctionKeys } from 'ts-gems';
88
+
89
+ interface Service {
90
+ name: string;
91
+ start(): void;
92
+ }
93
+
94
+ type Methods = FunctionKeys<Service>; // 'start'
95
+ type Data = NonFunctionKeys<Service>; // 'name'
96
+ ```
97
+
98
+ ## `KeysOfTypes<T, X>` / `StrictKeysOfTypes<T, X>`
99
+
100
+ Returns the union of property names matching `X`, per the same rules as
101
+ [`PickTypes`](#picktypest-x)/[`StrictPickTypes`](#strictpicktypest-x)
102
+ respectively.
103
+
104
+ ```ts
105
+ import type { KeysOfTypes, StrictKeysOfTypes } from 'ts-gems';
106
+
107
+ interface Row {
108
+ a: number;
109
+ b: string;
110
+ c: unknown;
111
+ }
112
+
113
+ type Keys = KeysOfTypes<Row, number>; // 'a' | 'c' - `unknown` matches everything
114
+ type StrictKeys = StrictKeysOfTypes<Row, number>; // 'a' - `c` (unknown) excluded
115
+ ```