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.
- package/README.md +114 -2
- package/docs/api/combine.md +48 -0
- package/docs/api/dto.md +115 -0
- package/docs/api/helpers.md +97 -0
- package/docs/api/logical.md +48 -0
- package/docs/api/mutable.md +164 -0
- package/docs/api/non-nullable.md +68 -0
- package/docs/api/nullish.md +66 -0
- package/docs/api/omit-never.md +62 -0
- package/docs/api/omit-undefined.md +60 -0
- package/docs/api/omit.md +99 -0
- package/docs/api/opaque.md +56 -0
- package/docs/api/partial.md +135 -0
- package/docs/api/pick.md +115 -0
- package/docs/api/readonly.md +150 -0
- package/docs/api/required.md +146 -0
- package/docs/api/type-check.md +246 -0
- package/docs/api/types.md +238 -0
- package/docs/api.md +113 -0
- package/docs/logo.svg +43 -0
- package/lib/dto.d.ts +14 -12
- package/lib/helpers.d.ts +16 -15
- package/lib/mutable.d.ts +15 -17
- package/lib/non-nullable.d.ts +31 -23
- package/lib/nullish.d.ts +15 -11
- package/lib/omit-never.d.ts +15 -11
- package/lib/omit-undefined.d.ts +12 -10
- package/lib/omit.d.ts +37 -28
- package/lib/partial.d.ts +12 -11
- package/lib/pick.d.ts +46 -38
- package/lib/readonly.d.ts +91 -77
- package/lib/required.d.ts +91 -75
- package/lib/type-check.d.ts +10 -9
- package/package.json +17 -15
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Readonly
|
|
2
|
+
|
|
3
|
+
Source: [`lib/readonly.d.ts`](../../lib/readonly.d.ts)
|
|
4
|
+
|
|
5
|
+
Adds `readonly` modifiers — the mirror image of [Mutable](mutable.md). See
|
|
6
|
+
[the `Deep*`/`Deeper*` convention](../api.md#the-deep--deeper-convention) and
|
|
7
|
+
the runtime [`asReadonly` cast helpers](helpers.md#runtime-cast-helpers).
|
|
8
|
+
|
|
9
|
+
## `ReadonlySome<T, K>`
|
|
10
|
+
|
|
11
|
+
Marks only the properties named in `K` as `readonly`; every other property is
|
|
12
|
+
untouched.
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import type { ReadonlySome } from 'ts-gems';
|
|
16
|
+
|
|
17
|
+
interface Row {
|
|
18
|
+
id: number;
|
|
19
|
+
name: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type LockedId = ReadonlySome<Row, 'id'>;
|
|
23
|
+
// { readonly id: number; name: string }
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## `DeepReadonly<T>`
|
|
27
|
+
|
|
28
|
+
Makes every property `readonly`, recursively into nested objects. Array-typed
|
|
29
|
+
properties are left exactly as-is — use `DeeperReadonly` to also make array
|
|
30
|
+
elements readonly.
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import type { DeepReadonly } from 'ts-gems';
|
|
34
|
+
|
|
35
|
+
interface Config {
|
|
36
|
+
name: string;
|
|
37
|
+
server: { host: string; port: number };
|
|
38
|
+
tags: string[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
type FrozenConfig = DeepReadonly<Config>;
|
|
42
|
+
// {
|
|
43
|
+
// readonly name: string;
|
|
44
|
+
// readonly server: { readonly host: string; readonly port: number };
|
|
45
|
+
// readonly tags: string[]; // untouched - it's an array
|
|
46
|
+
// }
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## `DeeperReadonly<T>`
|
|
50
|
+
|
|
51
|
+
Like `DeepReadonly`, but also makes the element type of array-typed
|
|
52
|
+
properties readonly. Tuples are left untouched.
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import type { DeeperReadonly } from 'ts-gems';
|
|
56
|
+
|
|
57
|
+
interface Config {
|
|
58
|
+
servers: { host: string }[];
|
|
59
|
+
range: [number, number]; // tuple
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type FrozenConfig = DeeperReadonly<Config>;
|
|
63
|
+
// {
|
|
64
|
+
// readonly servers: { readonly host: string }[];
|
|
65
|
+
// readonly range: [number, number]; // tuple - unchanged
|
|
66
|
+
// }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
> Note: `DeeperReadonly` makes the **property** and the **element type**
|
|
70
|
+
> readonly; it does not turn the array itself into a `readonly T[]` — push/
|
|
71
|
+
> pop on the array reference is still possible unless you also apply
|
|
72
|
+
> `Readonly<...>` around the whole property yourself. This also means that
|
|
73
|
+
> if the _input_ property was already a `readonly T[]`, `DeeperReadonly`
|
|
74
|
+
> normalizes it back down to a plain `T[]` wrapper (only the element type's
|
|
75
|
+
> readonly-ness is guaranteed, not the wrapper's).
|
|
76
|
+
|
|
77
|
+
## `ReadonlyKeys<T>`
|
|
78
|
+
|
|
79
|
+
Returns the union of property names in `T` that **are** `readonly`
|
|
80
|
+
(`keyof PickReadonly<T>`).
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import type { ReadonlyKeys } from 'ts-gems';
|
|
84
|
+
|
|
85
|
+
interface Row {
|
|
86
|
+
readonly id: number;
|
|
87
|
+
name: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
type Locked = ReadonlyKeys<Row>; // 'id'
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## `PickReadonly<T>` / `OmitReadonly<T>`
|
|
94
|
+
|
|
95
|
+
Pick (or omit) only the properties that **are** `readonly`.
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import type { OmitReadonly, PickReadonly } from 'ts-gems';
|
|
99
|
+
|
|
100
|
+
interface Row {
|
|
101
|
+
readonly id: number;
|
|
102
|
+
name: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
type OnlyReadonly = PickReadonly<Row>; // { readonly id: number }
|
|
106
|
+
type OnlyMutable = OmitReadonly<Row>; // { name: string }
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## `DeepPickReadonly<T>` / `DeepOmitReadonly<T>`
|
|
110
|
+
|
|
111
|
+
The deep versions of `PickReadonly`/`OmitReadonly` — nested objects are
|
|
112
|
+
filtered the same way, recursively.
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import type { DeepOmitReadonly } from 'ts-gems';
|
|
116
|
+
|
|
117
|
+
interface Row {
|
|
118
|
+
readonly id: number;
|
|
119
|
+
meta: { readonly createdBy: string; label: string };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
type Editable = DeepOmitReadonly<Row>;
|
|
123
|
+
// { meta: { label: string } }
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
A mutable property is always kept by `DeepOmitReadonly`, no matter its value
|
|
127
|
+
type — including `null`:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
type I1 = { a: null; readonly b: null };
|
|
131
|
+
type Result = DeepOmitReadonly<I1>; // { a: null }
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## `DeeperPickReadonly<T>` / `DeeperOmitReadonly<T>`
|
|
135
|
+
|
|
136
|
+
Like `DeepPickReadonly`/`DeepOmitReadonly`, but also recurses into array
|
|
137
|
+
elements. Tuples are preserved as-is (never split into a homogeneous array).
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import type { DeeperOmitReadonly } from 'ts-gems';
|
|
141
|
+
|
|
142
|
+
interface Row {
|
|
143
|
+
readonly id: number;
|
|
144
|
+
tags: { readonly createdBy: string; label: string }[];
|
|
145
|
+
point: readonly [number, number];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
type Editable = DeeperOmitReadonly<Row>;
|
|
149
|
+
// { tags: { label: string }[]; point: readonly [number, number] }
|
|
150
|
+
```
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Required
|
|
2
|
+
|
|
3
|
+
Source: [`lib/required.d.ts`](../../lib/required.d.ts)
|
|
4
|
+
|
|
5
|
+
Makes properties required (and strips `undefined` from their type). See
|
|
6
|
+
[the `Deep*`/`Deeper*` convention](../api.md#the-deep--deeper-convention) and
|
|
7
|
+
the runtime [`asRequired` cast helpers](helpers.md#runtime-cast-helpers).
|
|
8
|
+
|
|
9
|
+
## `RequiredSome<T, K>`
|
|
10
|
+
|
|
11
|
+
Marks only the properties named in `K` as required; the rest of `T` is left
|
|
12
|
+
exactly as declared.
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import type { RequiredSome } from 'ts-gems';
|
|
16
|
+
|
|
17
|
+
interface UpdateUserInput {
|
|
18
|
+
name?: string;
|
|
19
|
+
email?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type CreateUserInput = RequiredSome<UpdateUserInput, 'name'>;
|
|
23
|
+
// { name: string; email?: string }
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## `DeepRequired<T>`
|
|
27
|
+
|
|
28
|
+
Like the built-in `Required<T>`, but also makes nested object properties
|
|
29
|
+
required, recursively. Array-typed properties are left as-is.
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import type { DeepRequired } from 'ts-gems';
|
|
33
|
+
|
|
34
|
+
interface Config {
|
|
35
|
+
name?: string;
|
|
36
|
+
server?: { host?: string; port?: number };
|
|
37
|
+
tags?: string[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type FullConfig = DeepRequired<Config>;
|
|
41
|
+
// {
|
|
42
|
+
// name: string;
|
|
43
|
+
// server: { host: string; port: number };
|
|
44
|
+
// tags: string[]; // required now, but its element type is untouched
|
|
45
|
+
// }
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## `DeeperRequired<T>`
|
|
49
|
+
|
|
50
|
+
Like `DeepRequired`, but also makes array element properties required.
|
|
51
|
+
Tuples are left untouched.
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import type { DeeperRequired } from 'ts-gems';
|
|
55
|
+
|
|
56
|
+
interface Config {
|
|
57
|
+
servers?: { host?: string }[];
|
|
58
|
+
range?: [number, number]; // tuple
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type FullConfig = DeeperRequired<Config>;
|
|
62
|
+
// {
|
|
63
|
+
// servers: { host: string }[];
|
|
64
|
+
// range: [number, number]; // tuple - unchanged
|
|
65
|
+
// }
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`DeeperRequired` keeps its array-awareness through every level of nesting —
|
|
69
|
+
an array several objects deep is still processed, not just one at the top:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
interface Nested {
|
|
73
|
+
a?: { b?: { c?: number }[] };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
type Result = DeeperRequired<Nested>;
|
|
77
|
+
// { a: { b: { c: number }[] } }
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## `RequiredKeys<T>`
|
|
81
|
+
|
|
82
|
+
Returns the union of property names in `T` that are required
|
|
83
|
+
(`keyof PickRequired<T>`).
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import type { RequiredKeys } from 'ts-gems';
|
|
87
|
+
|
|
88
|
+
interface Row {
|
|
89
|
+
id: number;
|
|
90
|
+
nickname?: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
type Required = RequiredKeys<Row>; // 'id'
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## `PickRequired<T>` / `OmitRequired<T>`
|
|
97
|
+
|
|
98
|
+
Pick (or omit) only the properties that are required.
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import type { OmitRequired, PickRequired } from 'ts-gems';
|
|
102
|
+
|
|
103
|
+
interface Row {
|
|
104
|
+
id: number;
|
|
105
|
+
nickname?: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
type OnlyRequired = PickRequired<Row>; // { id: number }
|
|
109
|
+
type OnlyOptional = OmitRequired<Row>; // { nickname?: string }
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## `DeepPickRequired<T>` / `DeepOmitRequired<T>`
|
|
113
|
+
|
|
114
|
+
The deep versions of `PickRequired`/`OmitRequired`. A property is only ever
|
|
115
|
+
recursed into when **it is itself optional** — a required property is
|
|
116
|
+
dropped by `DeepOmitRequired` outright, without inspecting what's inside it
|
|
117
|
+
(there would be nothing to "omit the required parts of", since the whole
|
|
118
|
+
property is required in the first place).
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import type { DeepOmitRequired } from 'ts-gems';
|
|
122
|
+
|
|
123
|
+
interface Row {
|
|
124
|
+
id: number; // required -> dropped entirely
|
|
125
|
+
meta?: { label?: string; note: string }; // optional -> kept & recursed
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
type Optional = DeepOmitRequired<Row>;
|
|
129
|
+
// { meta?: { label?: string } } - `note` was required, so it's dropped too
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## `DeeperPickRequired<T>` / `DeeperOmitRequired<T>`
|
|
133
|
+
|
|
134
|
+
Like `DeepPickRequired`/`DeepOmitRequired`, but also recurses into array
|
|
135
|
+
elements. Tuples are preserved as-is.
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import type { DeeperOmitRequired } from 'ts-gems';
|
|
139
|
+
|
|
140
|
+
interface Row {
|
|
141
|
+
tags?: { label?: string; note: string }[];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
type Optional = DeeperOmitRequired<Row>;
|
|
145
|
+
// { tags?: { label?: string }[] }
|
|
146
|
+
```
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# Type Guards
|
|
2
|
+
|
|
3
|
+
Source: [`lib/type-check.d.ts`](../../lib/type-check.d.ts)
|
|
4
|
+
|
|
5
|
+
Every type below follows the same shape:
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
type IfX<T, Y = true, N = false> = /* ... */;
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
It checks a condition about `T` and resolves to `Y` when the condition holds,
|
|
12
|
+
`N` otherwise. `Y`/`N` default to the literal types `true`/`false`, which lets
|
|
13
|
+
you use them directly as compile-time assertions:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import type { IfNever } from 'ts-gems';
|
|
17
|
+
|
|
18
|
+
type Check = IfNever<never>; // true
|
|
19
|
+
type Check2 = IfNever<string>; // false
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
...or pass real types through `Y`/`N` to build a conditional transform:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
type OrString<T> = IfNever<T, string, T>; // replace `never` with `string`
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## The `*OrAny` variants
|
|
29
|
+
|
|
30
|
+
`IfTupleOrAny`, `IfPrimitiveOrAny`, `IfObjectOrAny`, `IfFunctionOrAny`, and
|
|
31
|
+
`IfClassOrAny` are convenience wrappers that treat `any` as an automatic
|
|
32
|
+
match, since `any` would otherwise fail every structural check:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
type IfXOrAny<T, Y = true, N = false> =
|
|
36
|
+
IfAny<T> extends true ? Y : IfX<T, Y, N>;
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import type { IfObject, IfObjectOrAny } from 'ts-gems';
|
|
41
|
+
|
|
42
|
+
type A = IfObject<any>; // false - `any` doesn't structurally match "object"
|
|
43
|
+
type B = IfObjectOrAny<any>; // true
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## `IfAny<T, Y, N>`
|
|
47
|
+
|
|
48
|
+
Detects the `any` type specifically — distinct from `unknown`, `never`, and
|
|
49
|
+
every other type. Uses the well-known `0 extends 1 & T` trick, since `any` is
|
|
50
|
+
the only type where this holds.
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import type { IfAny } from 'ts-gems';
|
|
54
|
+
|
|
55
|
+
type A = IfAny<any>; // true
|
|
56
|
+
type B = IfAny<unknown>; // false
|
|
57
|
+
type C = IfAny<never>; // false
|
|
58
|
+
type D = IfAny<string>; // false
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## `IfNever<T, Y, N>`
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import type { IfNever } from 'ts-gems';
|
|
65
|
+
|
|
66
|
+
type A = IfNever<never>; // true
|
|
67
|
+
type B = IfNever<undefined>; // false
|
|
68
|
+
type C = IfNever<any>; // false
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## `IfUndefined<T, Y, N>` / `IfNull<T, Y, N>` / `IfUnknown<T, Y, N>`
|
|
72
|
+
|
|
73
|
+
Exact-type checks for `undefined`, `null`, and `unknown` respectively, each
|
|
74
|
+
implemented via [`IfEquals`](#ifequalst1-t2-y-n).
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import type { IfNull, IfUndefined, IfUnknown } from 'ts-gems';
|
|
78
|
+
|
|
79
|
+
type A = IfUndefined<undefined>; // true
|
|
80
|
+
type B = IfUndefined<null>; // false
|
|
81
|
+
|
|
82
|
+
type C = IfNull<null>; // true
|
|
83
|
+
type D = IfNull<undefined>; // false
|
|
84
|
+
|
|
85
|
+
type E = IfUnknown<unknown>; // true
|
|
86
|
+
type F = IfUnknown<any>; // false - `any` is not `unknown`
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## `IfNullish<T, Y, N>`
|
|
90
|
+
|
|
91
|
+
`true` for `null` **or** `undefined`.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import type { IfNullish } from 'ts-gems';
|
|
95
|
+
|
|
96
|
+
type A = IfNullish<null>; // true
|
|
97
|
+
type B = IfNullish<undefined>; // true
|
|
98
|
+
type C = IfNullish<0>; // false
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## `IfSymbol<T, Y, N>`
|
|
102
|
+
|
|
103
|
+
`true` only for the general `symbol` type — **not** for a specific `unique
|
|
104
|
+
symbol` (e.g. the type of a `const x = Symbol('x')`), which is a distinct,
|
|
105
|
+
narrower type.
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import type { IfSymbol } from 'ts-gems';
|
|
109
|
+
|
|
110
|
+
const uniqueSym = Symbol('x');
|
|
111
|
+
|
|
112
|
+
type A = IfSymbol<symbol>; // true
|
|
113
|
+
type B = IfSymbol<typeof uniqueSym>; // false - a *specific* unique symbol
|
|
114
|
+
type C = IfSymbol<string>; // false
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## `IfTuple<T, Y, N>`
|
|
118
|
+
|
|
119
|
+
`true` for a fixed-length tuple (`[string, number]`, `[]`, ...); `false` for
|
|
120
|
+
a variable-length array (`string[]`) or anything else. Distinguishes the two
|
|
121
|
+
by checking whether `T['length']` is a literal number (tuple) or the general
|
|
122
|
+
`number` type (array).
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import type { IfTuple } from 'ts-gems';
|
|
126
|
+
|
|
127
|
+
type A = IfTuple<[string, number]>; // true
|
|
128
|
+
type B = IfTuple<[]>; // true
|
|
129
|
+
type C = IfTuple<string[]>; // false
|
|
130
|
+
type D = IfTuple<any>; // false
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
> This is the type that decides, throughout the whole library, whether a
|
|
134
|
+
> tuple gets left untouched by `Deeper*` transforms instead of being
|
|
135
|
+
> collapsed into a homogeneous array — see [the `Deep*`/`Deeper*`
|
|
136
|
+
> convention](../api.md#the-deep--deeper-convention).
|
|
137
|
+
|
|
138
|
+
## `IfPrimitive<T, Y, N>`
|
|
139
|
+
|
|
140
|
+
`true` for any [`Primitive`](types.md#primitive): `string | number | bigint |
|
|
141
|
+
boolean | symbol | null | undefined`, plus `any`. `false` for objects,
|
|
142
|
+
arrays, functions, and classes.
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import type { IfPrimitive } from 'ts-gems';
|
|
146
|
+
|
|
147
|
+
type A = IfPrimitive<string>; // true
|
|
148
|
+
type B = IfPrimitive<null>; // true
|
|
149
|
+
type C = IfPrimitive<{}>; // false
|
|
150
|
+
type D = IfPrimitive<Function>; // false
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## `IfEmptyObject<T, Y, N>`
|
|
154
|
+
|
|
155
|
+
`true` only for the exact type `{}`.
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
import type { IfEmptyObject } from 'ts-gems';
|
|
159
|
+
|
|
160
|
+
type A = IfEmptyObject<{}>; // true
|
|
161
|
+
type B = IfEmptyObject<{ x: 1 }>; // false
|
|
162
|
+
type C = IfEmptyObject<undefined>; // false
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## `IfObject<T, Y, N>`
|
|
166
|
+
|
|
167
|
+
`true` for anything structurally an `object` that isn't a primitive,
|
|
168
|
+
`Function`, or array (arrays get their own check via [`IfTuple`](#iftuplet-y-n)
|
|
169
|
+
or a plain `T[]` test elsewhere in the library).
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
import type { IfObject } from 'ts-gems';
|
|
173
|
+
|
|
174
|
+
type A = IfObject<{ x: 1 }>; // true
|
|
175
|
+
type B = IfObject<object>; // true
|
|
176
|
+
type C = IfObject<any[]>; // false
|
|
177
|
+
type D = IfObject<Function>; // false
|
|
178
|
+
type E = IfObject<null>; // false
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## `IfFunction<T, Y, N>`
|
|
182
|
+
|
|
183
|
+
`true` for any function type (including arrow functions), `false` for a
|
|
184
|
+
constructor/class reference (see [`IfClass`](#ifclasst-y-n) for that case).
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
import type { IfFunction } from 'ts-gems';
|
|
188
|
+
|
|
189
|
+
class Foo {}
|
|
190
|
+
|
|
191
|
+
type A = IfFunction<() => void>; // true
|
|
192
|
+
type B = IfFunction<(a: string) => boolean>; // true
|
|
193
|
+
type C = IfFunction<typeof Foo>; // false - a constructor, not a plain function
|
|
194
|
+
type D = IfFunction<Foo>; // false - an instance
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## `IfClass<T, Y, N>`
|
|
198
|
+
|
|
199
|
+
`true` when `T` is a constructor/class reference — i.e. it matches
|
|
200
|
+
[`Type<any>`](types.md#typet--any) (`new (...args: any[]) => any`).
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
import type { IfClass } from 'ts-gems';
|
|
204
|
+
|
|
205
|
+
class Foo {}
|
|
206
|
+
function bar() {}
|
|
207
|
+
|
|
208
|
+
type A = IfClass<typeof Foo>; // true
|
|
209
|
+
type B = IfClass<typeof bar>; // false
|
|
210
|
+
type C = IfClass<Foo>; // false - an instance is not a constructor
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## `IfEquals<T1, T2, Y, N>`
|
|
214
|
+
|
|
215
|
+
Exact, deeply-structural type equality — the classic
|
|
216
|
+
[Matt McCutchen conditional-type trick](https://github.com/Microsoft/TypeScript/issues/27024#issuecomment-421529650),
|
|
217
|
+
extended in this library to also compare object shapes recursively. Two
|
|
218
|
+
types are equal only if they are mutually and exactly assignable, including
|
|
219
|
+
modifiers (`?`, `readonly`).
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
import type { IfEquals } from 'ts-gems';
|
|
223
|
+
|
|
224
|
+
type A = IfEquals<number, number>; // true
|
|
225
|
+
type B = IfEquals<number, number | string>; // false
|
|
226
|
+
type C = IfEquals<{ x: 1 }, { x?: 1 }>; // false - optionality differs
|
|
227
|
+
type D = IfEquals<any, any>; // true
|
|
228
|
+
type E = IfEquals<any, unknown>; // false
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## `IfCompatible<T1, T2, Y, N>`
|
|
232
|
+
|
|
233
|
+
A looser, one- or two-way _assignability_ check (unlike `IfEquals`, which
|
|
234
|
+
requires exact equality). Used internally to compare function parameter/return
|
|
235
|
+
compatibility, and handles `any`/`unknown`/`never`/`null`/`undefined`
|
|
236
|
+
specially so they behave as you'd expect at the call site rather than by
|
|
237
|
+
strict structural rules.
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
import type { IfCompatible } from 'ts-gems';
|
|
241
|
+
|
|
242
|
+
type A = IfCompatible<number, number | string>; // true
|
|
243
|
+
type B = IfCompatible<any, number>; // true
|
|
244
|
+
type C = IfCompatible<{}, number>; // false
|
|
245
|
+
type D = IfCompatible<() => void, (a: string) => void>; // true
|
|
246
|
+
```
|