import type {Primitive} from './primitive.d.ts'; import type {_Numeric} from './numeric.d.ts'; import type {CollapseLiterals, IfNotAnyOrNever, IsNotFalse, IsPrimitive} from './internal/index.d.ts'; import type {IsNever} from './is-never.d.ts'; import type {TagContainer, UnwrapTagged} from './tagged.d.ts'; /** 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 //=> false LiteralCheck<1, string> //=> false ``` */ type LiteralCheck = ( IsNever 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 //=> false ``` */ type LiteralChecks = ( // Conditional type to force union distribution. // If `T` is none of the literal types in the union `LiteralUnionType`, then `LiteralCheck` 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 : 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 The implementation of this type is inspired by the trick mentioned in this [StackOverflow answer](https://stackoverflow.com/a/68261113/420747). @example ``` import type {IsStringLiteral} from 'type-fest'; type CapitalizedString = IsStringLiteral extends true ? Capitalize : string; // https://github.com/yankeeinlondon/native-dash/blob/master/src/capitalize.ts function capitalize>(input: T): CapitalizedString { return (input.slice(0, 1).toUpperCase() + input.slice(1)) as CapitalizedString; } const output = capitalize('hello, world!'); //=> 'Hello, world!' ``` @example ``` // String types with infinite set of possible values return `false`. import type {IsStringLiteral} from 'type-fest'; type AllUppercaseStrings = IsStringLiteral>; //=> false type StringsStartingWithOn = IsStringLiteral<`on${string}`>; //=> false // This behaviour is particularly useful in string manipulation utilities, as infinite string types often require separate handling. type Length = IsStringLiteral extends false ? number // return `number` for infinite string types : S extends `${string}${infer Tail}` ? Length : Counter['length']; type L1 = Length>; //=> number type L2 = Length<`${number}`>; //=> number ``` @category Type Guard @category Utilities */ export type IsStringLiteral = IfNotAnyOrNever ? UnwrapTagged : S>>, false, false>; export type _IsStringLiteral = // If `T` is an infinite string type (e.g., `on${string}`), `Record` produces an index signature, // and since `{}` extends index signatures, the result becomes `false`. S extends string ? {} extends Record ? false : true : false; /** 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/modules/types/src/boolean-logic/operators/EndsWith.ts type EndsWith = TValue extends string ? IsStringLiteral extends true ? IsStringLiteral extends true ? TValue extends `${string}${TEndsWith}` ? true : false : boolean : boolean : TValue extends number ? IsNumericLiteral extends true ? EndsWith<`${TValue}`, TEndsWith> : false : false; function endsWith(input: Input, end: End) { return `${input}`.endsWith(end) as EndsWith; } 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 = LiteralChecks; /** 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 = IsBooleanLiteral extends true ? AsString extends true ? `${typeof id}` : typeof id : number | string; function getId(options?: {asString: AsString}) { return (options?.asString ? `${id}` : id) as GetId; } 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 = LiteralCheck; /** 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, Key extends keyof Obj> = IsSymbolLiteral extends true ? Obj[Key] : number; function get, Key extends keyof Obj>(o: Obj, key: Key) { return o[key] as Get; } 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 = LiteralCheck; /** Helper type for `IsLiteral`. */ type IsLiteralUnion = | IsStringLiteral | IsNumericLiteral | IsBooleanLiteral | IsSymbolLiteral; /** 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'; type A = IsLiteral<1>; //=> true type B = IsLiteral; //=> false type C = IsLiteral<1n>; //=> true type D = IsLiteral; //=> false type E = IsLiteral<'type-fest'>; //=> true type F = IsLiteral; //=> false type G = IsLiteral<`on${string}`>; //=> false declare const symbolLiteral: unique symbol; type H = IsLiteral; //=> true type I = IsLiteral; //=> false type J = IsLiteral; //=> true type K = IsLiteral; //=> false ``` @category Type Guard @category Utilities */ export type IsLiteral = IsPrimitive extends true ? IsNotFalse> : false; export {};