-
-
Notifications
You must be signed in to change notification settings - Fork 548
/
is-literal.d.ts
258 lines (203 loc) · 6.78 KB
/
is-literal.d.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
import type {Primitive} from './primitive';
import type {Numeric} from './numeric';
import type {IsNotFalse, IsPrimitive} from './internal';
import type {IsNever} from './is-never';
/**
Returns a boolean for whether the given type `T` is the specified `LiteralType`.
@link https://stackoverflow.com/a/52806744/10292952
@example
```
LiteralCheck<1, number>
//=> true
LiteralCheck<number, number>
//=> false
LiteralCheck<1, string>
//=> false
```
*/
type LiteralCheck<T, LiteralType extends Primitive> = (
IsNever<T> extends false // Must be wider than `never`
? [T] extends [LiteralType & infer U] // Remove any branding
? [U] extends [LiteralType] // Must be narrower than `LiteralType`
? [LiteralType] extends [U] // Cannot be wider than `LiteralType`
? false
: true
: false
: false
: false
);
/**
Returns a boolean for whether the given type `T` is one of the specified literal types in `LiteralUnionType`.
@example
```
LiteralChecks<1, Numeric>
//=> true
LiteralChecks<1n, Numeric>
//=> true
LiteralChecks<bigint, Numeric>
//=> false
```
*/
type LiteralChecks<T, LiteralUnionType> = (
// Conditional type to force union distribution.
// If `T` is none of the literal types in the union `LiteralUnionType`, then `LiteralCheck<T, LiteralType>` will evaluate to `false` for the whole union.
// If `T` is one of the literal types in the union, it will evaluate to `boolean` (i.e. `true | false`)
IsNotFalse<LiteralUnionType extends Primitive
? LiteralCheck<T, LiteralUnionType>
: never
>
);
/**
Returns a boolean for whether the given type is a `string` [literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types).
Useful for:
- providing strongly-typed string manipulation functions
- constraining strings to be a string literal
- type utilities, such as when constructing parsers and ASTs
@example
```
import type {IsStringLiteral} from 'type-fest';
type CapitalizedString<T extends string> = IsStringLiteral<T> extends true ? Capitalize<T> : string;
// https://github.com/yankeeinlondon/native-dash/blob/master/src/capitalize.ts
function capitalize<T extends Readonly<string>>(input: T): CapitalizedString<T> {
return (input.slice(0, 1).toUpperCase() + input.slice(1)) as CapitalizedString<T>;
}
const output = capitalize('hello, world!');
//=> 'Hello, world!'
```
@category Type Guard
@category Utilities
*/
export type IsStringLiteral<T> = LiteralCheck<T, string>;
/**
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).
Useful for:
- providing strongly-typed functions when given literal arguments
- type utilities, such as when constructing parsers and ASTs
@example
```
import type {IsNumericLiteral} from 'type-fest';
// https://github.com/inocan-group/inferred-types/blob/master/src/types/boolean-logic/EndsWith.ts
type EndsWith<TValue, TEndsWith extends string> =
TValue extends string
? IsStringLiteral<TEndsWith> extends true
? IsStringLiteral<TValue> extends true
? TValue extends `${string}${TEndsWith}`
? true
: false
: boolean
: boolean
: TValue extends number
? IsNumericLiteral<TValue> extends true
? EndsWith<`${TValue}`, TEndsWith>
: false
: false;
function endsWith<Input extends string | number, End extends string>(input: Input, end: End) {
return `${input}`.endsWith(end) as EndsWith<Input, End>;
}
endsWith('abc', 'c');
//=> true
endsWith(123456, '456');
//=> true
const end = '123' as string;
endsWith('abc123', end);
//=> boolean
```
@category Type Guard
@category Utilities
*/
export type IsNumericLiteral<T> = LiteralChecks<T, Numeric>;
/**
Returns a boolean for whether the given type is a `true` or `false` [literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types).
Useful for:
- providing strongly-typed functions when given literal arguments
- type utilities, such as when constructing parsers and ASTs
@example
```
import type {IsBooleanLiteral} from 'type-fest';
const id = 123;
type GetId<AsString extends boolean> =
IsBooleanLiteral<AsString> extends true
? AsString extends true
? `${typeof id}`
: typeof id
: number | string;
function getId<AsString extends boolean = false>(options?: {asString: AsString}) {
return (options?.asString ? `${id}` : id) as GetId<AsString>;
}
const numberId = getId();
//=> 123
const stringId = getId({asString: true});
//=> '123'
declare const runtimeBoolean: boolean;
const eitherId = getId({asString: runtimeBoolean});
//=> number | string
```
@category Type Guard
@category Utilities
*/
export type IsBooleanLiteral<T> = LiteralCheck<T, boolean>;
/**
Returns a boolean for whether the given type is a `symbol` [literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types).
Useful for:
- providing strongly-typed functions when given literal arguments
- type utilities, such as when constructing parsers and ASTs
@example
```
import type {IsSymbolLiteral} from 'type-fest';
type Get<Obj extends Record<symbol, number>, Key extends keyof Obj> =
IsSymbolLiteral<Key> extends true
? Obj[Key]
: number;
function get<Obj extends Record<symbol, number>, Key extends keyof Obj>(o: Obj, key: Key) {
return o[key] as Get<Obj, Key>;
}
const symbolLiteral = Symbol('literal');
const symbolValue: symbol = Symbol('value');
get({[symbolLiteral]: 1} as const, symbolLiteral);
//=> 1
get({[symbolValue]: 1} as const, symbolValue);
//=> number
```
@category Type Guard
@category Utilities
*/
export type IsSymbolLiteral<T> = LiteralCheck<T, symbol>;
/** Helper type for `IsLiteral`. */
type IsLiteralUnion<T> =
| IsStringLiteral<T>
| IsNumericLiteral<T>
| IsBooleanLiteral<T>
| IsSymbolLiteral<T>;
/**
Returns a boolean for whether the given type is a [literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types).
Useful for:
- providing strongly-typed functions when given literal arguments
- type utilities, such as when constructing parsers and ASTs
@example
```
import type {IsLiteral} from 'type-fest';
// https://github.com/inocan-group/inferred-types/blob/master/src/types/string-literals/StripLeading.ts
export type StripLeading<A, B> =
A extends string
? B extends string
? IsLiteral<A> extends true
? string extends B ? never : A extends `${B & string}${infer After}` ? After : A
: string
: A
: A;
function stripLeading<Input extends string, Strip extends string>(input: Input, strip: Strip) {
return input.replace(`^${strip}`, '') as StripLeading<Input, Strip>;
}
stripLeading('abc123', 'abc');
//=> '123'
const str = 'abc123' as string;
stripLeading(str, 'abc');
//=> string
```
@category Type Guard
@category Utilities
*/
export type IsLiteral<T> =
IsPrimitive<T> extends true
? IsNotFalse<IsLiteralUnion<T>>
: false;