UNPKG

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