Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add IsXLiteral types #563

Merged
merged 24 commits into from Mar 10, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
84dd90f
feat: add `IsXLiteral` types
tommy-mitchell Mar 6, 2023
6769a2f
fix: ordering
tommy-mitchell Mar 6, 2023
e4ecfed
chore: remove `IsNull` and `IsUndefined`
tommy-mitchell Mar 6, 2023
79c94a9
fix: missing cases
tommy-mitchell Mar 6, 2023
9a325cb
fix: remove unused type helper
tommy-mitchell Mar 6, 2023
921b11d
feat(`internal`): add `IsNotFalse` type
tommy-mitchell Mar 7, 2023
3537f3b
feat: simplify literal checks
tommy-mitchell Mar 7, 2023
e593486
fix: formatting
tommy-mitchell Mar 7, 2023
4c92b2f
feat: simplify `LiteralCheck`
tommy-mitchell Mar 7, 2023
ecb3d13
feat: simplify `IsNumericLiteral`
tommy-mitchell Mar 7, 2023
da3c48e
feat: scaffold doc comments
tommy-mitchell Mar 7, 2023
13c308f
feat: `IsStringLiteral` documentation
tommy-mitchell Mar 7, 2023
04e690a
fix: add source of example
tommy-mitchell Mar 7, 2023
ef12d12
chore: scaffold more doc comments
tommy-mitchell Mar 7, 2023
e302d94
fix(`test-d`): reorder test cases
tommy-mitchell Mar 7, 2023
d9f3208
feat(`IsLiteral`): add example
tommy-mitchell Mar 8, 2023
bbdcbb4
docs(`IsStringLiteral`): use cases
tommy-mitchell Mar 8, 2023
a6cc701
docs: unify use cases
tommy-mitchell Mar 8, 2023
1af5f31
docs: document type helpers
tommy-mitchell Mar 8, 2023
a5b806a
docs(`IsNumericLiteral`): add example
tommy-mitchell Mar 8, 2023
0b75c16
fix(`IsNumericLiteral`): use cases
tommy-mitchell Mar 8, 2023
5f7ca08
feat(`IsBooleanLiteral`): add example
tommy-mitchell Mar 8, 2023
df6f026
docs(`IsSymbolLiteral`): add example
tommy-mitchell Mar 9, 2023
bded982
chore: add `Type Guard` category
tommy-mitchell Mar 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions index.d.ts
Expand Up @@ -76,6 +76,15 @@ export type {HasRequiredKeys} from './source/has-required-keys';
export type {Spread} from './source/spread';
export type {TupleToUnion} from './source/tuple-to-union';
export type {IsEqual} from './source/is-equal';
export type {
IsLiteral,
IsStringLiteral,
IsNumericLiteral,
IsBooleanLiteral,
IsSymbolLiteral,
IsUndefinedLiteral,
IsNullLiteral,
} from './source/is-literal';

// Template literal types
export type {CamelCase} from './source/camel-case';
Expand Down
7 changes: 7 additions & 0 deletions readme.md
Expand Up @@ -171,6 +171,13 @@ Click the type names for complete docs.
- [`HasRequiredKeys`](source/has-required-keys.d.ts) - Create a `true`/`false` type depending on whether the given type has any required fields.
- [`Spread`](source/spread.d.ts) - Mimic the type inferred by TypeScript when merging two objects or two arrays/tuples using the spread syntax.
- [`IsEqual`](source/is-equal.d.ts) - Returns a boolean for whether the two given types are equal.
- [`IsLiteral`](source/is-literal.d.ts) - Returns a boolean for whether the given type is a [literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types).
- [`IsStringLiteral`](source/is-literal.d.ts) - 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).
- [`IsNumericLiteral`](source/is-literal.d.ts) - 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).
- [`IsBooleanLiteral`](source/is-literal.d.ts) - 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).
- [`IsSymbolLiteral`](source/is-literal.d.ts) - 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).
- [`IsNullLiteral`](source/is-literal.d.ts) - Returns a boolean for whether the given type is a `null` [literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types).
- [`IsUndefinedLiteral`](source/is-literal.d.ts) - Returns a boolean for whether the given type is an `undefined` [literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types).

### JSON

Expand Down
59 changes: 59 additions & 0 deletions source/is-literal.d.ts
@@ -0,0 +1,59 @@
import type {Numeric} from './numeric';
import type {Primitive} from './primitive';
import type {Includes} from './includes';

/** @link https://stackoverflow.com/a/52806744/10292952 */
type LiteralCheck<T, LiteralType extends Primitive> = (
[T] extends [never] // Must be wider than `never`
? false
: T extends LiteralType // Must be narrower than `LiteralType`
? LiteralType extends T // Cannot be wider than `LiteralType`
? false
: true
: false
);

type StringifiedLiteralCheck<T, LiteralType extends null | undefined> = (
[T] extends [never] // Must be wider than `never`
? false
: T extends LiteralType // Safe stringify
? `${T}` extends `${LiteralType}` // Must be narrower than `${LiteralType}`
? true
: false
: false
);

export type IsStringLiteral<T> = LiteralCheck<T, string>;

export type IsNumericLiteral<T> = Includes<[LiteralCheck<T, number>, LiteralCheck<T, bigint>], true>;

export type IsBooleanLiteral<T> = (
[T] extends [never] // Must be wider than `never`
? false
: [T] extends [true]
? boolean extends T // Must be narrower than `boolean`
? false
: true
: [T] extends [false]
? boolean extends T // Must be narrower than `boolean`
? false
: true
: false
);

export type IsSymbolLiteral<T> = LiteralCheck<T, symbol>;

export type IsNullLiteral<T> = StringifiedLiteralCheck<T, null>;

export type IsUndefinedLiteral<T> = StringifiedLiteralCheck<T, undefined>;

type IsLiteralTuple<T> = [
IsStringLiteral<T>,
IsNumericLiteral<T>,
IsBooleanLiteral<T>,
IsSymbolLiteral<T>,
IsNullLiteral<T>,
IsUndefinedLiteral<T>,
];

export type IsLiteral<T extends Primitive> = Includes<IsLiteralTuple<T>, true>;
tommy-mitchell marked this conversation as resolved.
Show resolved Hide resolved
76 changes: 76 additions & 0 deletions test-d/is-literal.ts
@@ -0,0 +1,76 @@
import {expectError, expectType} from 'tsd';
import type {
IsLiteral,
IsStringLiteral,
IsNumericLiteral,
IsBooleanLiteral,
IsSymbolLiteral,
IsUndefinedLiteral,
IsNullLiteral,
} from '../index';

const stringLiteral = '';
const numberLiteral = 1;
// @ts-expect-error (suppress BigInt literal tsd warning)
const bigintLiteral = 1n;
const booleanLiteral = true;
const symbolLiteral = Symbol('');

declare const _string: string;
declare const _number: number;
declare const _bigint: bigint;
declare const _boolean: boolean;
declare const _symbol: symbol;
declare const _null: null;
declare const _undefined: undefined;

// Literals should be true
expectType<IsLiteral<typeof stringLiteral>>(true);
expectType<IsLiteral<typeof numberLiteral>>(true);
expectType<IsLiteral<typeof bigintLiteral>>(true);
expectType<IsLiteral<typeof booleanLiteral>>(true);
expectType<IsLiteral<typeof symbolLiteral>>(true);
expectType<IsLiteral<null>>(true);
expectType<IsLiteral<undefined>>(true);

// Primitives and others should be false
expectType<IsLiteral<typeof _string>>(false);
expectType<IsLiteral<typeof _number>>(false);
expectType<IsLiteral<typeof _bigint>>(false);
expectType<IsLiteral<typeof _boolean>>(false);
expectType<IsLiteral<typeof _symbol>>(false);
// Fix: expectType<IsLiteral<typeof _null>>(false);
// Fix: expectType<IsLiteral<typeof _undefined>>(false);
expectType<IsLiteral<any>>(false);
expectType<IsLiteral<never>>(false);

expectType<IsStringLiteral<typeof stringLiteral>>(true);
expectType<IsStringLiteral<typeof _string>>(false);

expectType<IsNumericLiteral<typeof numberLiteral>>(true);
expectType<IsNumericLiteral<typeof bigintLiteral>>(true);
expectType<IsNumericLiteral<typeof _number>>(false);
expectType<IsNumericLiteral<typeof _bigint>>(false);

expectType<IsBooleanLiteral<typeof booleanLiteral>>(true);
expectType<IsBooleanLiteral<typeof _boolean>>(false);

expectType<IsSymbolLiteral<typeof symbolLiteral>>(true);
expectType<IsSymbolLiteral<typeof _symbol>>(false);

expectType<IsNullLiteral<null>>(true);
// Fix: expectType<IsNullLiteral<typeof _null>>(false);

expectType<IsUndefinedLiteral<undefined>>(true);
// Fix: expectType<IsUndefinedLiteral<typeof _undefined>>(false);

declare const anything: any;

// Missing generic parameter
expectError<IsLiteral>(anything);
expectError<IsStringLiteral>(anything);
expectError<IsNumericLiteral>(anything);
expectError<IsBooleanLiteral>(anything);
expectError<IsSymbolLiteral>(anything);
expectError<IsNullLiteral>(anything);
expectError<IsUndefinedLiteral>(anything);