From 344a1b2a5eaa03ca15bc142e53f5d73a55c814bc Mon Sep 17 00:00:00 2001 From: Tommy Date: Sun, 5 Mar 2023 19:37:14 -0600 Subject: [PATCH 01/26] feat: add `IsX`/`IfX` types for `any`/`never`/ `unknown` --- index.d.ts | 3 +++ readme.md | 6 +++++ source/internal.d.ts | 18 +------------ source/is-if-any.d.ts | 53 +++++++++++++++++++++++++++++++++++++ source/is-if-never.d.ts | 35 ++++++++++++++++++++++++ source/is-if-unknown.d.ts | 40 ++++++++++++++++++++++++++++ source/jsonify.d.ts | 3 ++- source/set-return-type.d.ts | 2 +- test-d/is-if-any.ts | 27 +++++++++++++++++++ test-d/is-if-never.ts | 27 +++++++++++++++++++ test-d/is-if-unknown.ts | 27 +++++++++++++++++++ 11 files changed, 222 insertions(+), 19 deletions(-) create mode 100644 source/is-if-any.d.ts create mode 100644 source/is-if-never.d.ts create mode 100644 source/is-if-unknown.d.ts create mode 100644 test-d/is-if-any.ts create mode 100644 test-d/is-if-never.ts create mode 100644 test-d/is-if-unknown.ts diff --git a/index.d.ts b/index.d.ts index 5770e8731..a5de93db9 100644 --- a/index.d.ts +++ b/index.d.ts @@ -76,6 +76,9 @@ 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 {IsAny, IfAny} from './source/is-if-any'; +export type {IsNever, IfNever} from './source/is-if-never'; +export type {IsUnknown, IfUnknown} from './source/is-if-unknown'; // Template literal types export type {CamelCase} from './source/camel-case'; diff --git a/readme.md b/readme.md index 245862986..0d732ac88 100644 --- a/readme.md +++ b/readme.md @@ -171,6 +171,12 @@ 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. +- [`IsAny`](source/is-if-any.d.ts) - Returns a boolean for whether the given type is `any`. +- [`IfAny`](source/is-if-any.d.ts) - An `If`/`Else` like type that resolves whether the given type is `any`. +- [`IsNever`](source/is-if-never.d.ts) - Returns a boolean for whether the given type is `never`. +- [`IfNever`](source/is-if-never.d.ts) - An `If`/`Else` like type that resolves whether the given type is `never`. +- [`IsUnknown`](source/is-if-unknown.d.ts) - Returns a boolean for whether the given type is `unknown`. +- [`IfUnknown`](source/is-if-unknown.d.ts) - An `If`/`Else` like type that resolves whether the given type is `unknown`. ### JSON diff --git a/source/internal.d.ts b/source/internal.d.ts index 46c2d0920..049b19333 100644 --- a/source/internal.d.ts +++ b/source/internal.d.ts @@ -1,6 +1,7 @@ import type {Primitive} from './primitive'; import type {Simplify} from './simplify'; import type {Trim} from './trim'; +import type {IsAny} from './is-if-any'; /** Infer the length of the given array ``. @@ -158,23 +159,6 @@ export type IsNumeric = T extends `${number}` : false : false; -/** -Returns a boolean for whether the the type is `any`. - -@link https://stackoverflow.com/a/49928360/1490091 -*/ -export type IsAny = 0 extends 1 & T ? true : false; - -/** -Returns a boolean for whether the the type is `never`. -*/ -export type IsNever = [T] extends [never] ? true : false; - -/** -Returns a boolean for whether the the type is `unknown`. -*/ -export type IsUnknown = IsNever extends false ? T extends unknown ? unknown extends T ? IsAny extends false ? true : false : false : false : false; - /** For an object T, if it has any properties that are a union with `undefined`, make those into optional properties instead. diff --git a/source/is-if-any.d.ts b/source/is-if-any.d.ts new file mode 100644 index 000000000..40c418adb --- /dev/null +++ b/source/is-if-any.d.ts @@ -0,0 +1,53 @@ +/** +Returns a boolean for whether the given type is `any`. + +@link https://stackoverflow.com/a/49928360/1490091 + +Useful for disallowing `any`s to be passed to a function or used in a type utility. + +@example +``` +import type {IsAny} from 'type-fest'; + +type Must = ( + IsAny extends false + ? IsNever extends false + ? IsUnknown extends false + ? IsUndefined extends false + ? IsNull extends false + ? T + : never + : never + : never + : never + : never +); +``` + +@category Utilities +*/ +export type IsAny = 0 extends 1 & T ? true : false; + +/** +If the given type `T` is `any`, the returned type is `TypeIfAny`. Otherwise, +the return type is `TypeIfNotAny`. If only `T` is specified, `TypeIfAny` +will be `true` and `TypeIfNotAny` will be false. + +@link https://stackoverflow.com/a/49928360/1490091 + +Useful for disallowing `any`s to be passed to a function or used in a type utility. + +@example +``` +import type {IfAny} from 'type-fest'; + +function get>, K extends keyof O = keyof O>(obj: O, key: K) { + return obj[key]; +} +``` + +@category Utilities +*/ +export type IfAny = ( + IsAny extends true ? TypeIfAny : TypeIfNotAny +); diff --git a/source/is-if-never.d.ts b/source/is-if-never.d.ts new file mode 100644 index 000000000..2a92ccf7d --- /dev/null +++ b/source/is-if-never.d.ts @@ -0,0 +1,35 @@ +/** +Returns a boolean for whether the given type is `never`. + +@link https://github.com/microsoft/TypeScript/issues/31751#issuecomment-498526919 +@link https://stackoverflow.com/a/53984913/10292952 + +@example +``` +import type {IsNever} from 'type-fest'; + +``` + +@category Utilities +*/ +export type IsNever = [T] extends [never] ? true : false; + +/** +If the given type `T` is `never`, the returned type is `TypeIfNever`. Otherwise, +the return type is `TypeIfNotNever`. If only `T` is specified, `TypeIfNever` +will be `true` and `TypeIfNotNever` will be false. + +@link https://github.com/microsoft/TypeScript/issues/31751#issuecomment-498526919 +@link https://stackoverflow.com/a/53984913/10292952 + +@example +``` +import type {IfNever} from 'type-fest'; + +``` + +@category Utilities +*/ +export type IfNever = ( + IsNever extends true ? TypeIfNever : TypeIfNotNever +); diff --git a/source/is-if-unknown.d.ts b/source/is-if-unknown.d.ts new file mode 100644 index 000000000..ab3bc1466 --- /dev/null +++ b/source/is-if-unknown.d.ts @@ -0,0 +1,40 @@ +/** Returns a boolean for whether the given type is `null`. */ +type IsNull = [T] extends [null] ? true : false; + +/** +Returns a boolean for whether the given type is `unknown`. + +@link https://github.com/dsherret/conditional-type-checks/pull/16 + +@example +``` +import type {IsUnknown} from 'type-fest'; + +``` + +@category Utilities +*/ +export type IsUnknown = ( + unknown extends T // `T` can be `unknown` or `any` + ? IsNull extends false // `any` can be `null`, but `unknown` can't be + ? true + : false + : false +); + +/** +If the given type `T` is `unknown`, the returned type is `TypeIfUnknown`. +Otherwise, the return type is `TypeIfNotUnknown`. If only `T` is specified, +`TypeIfUnknown` will be `true` and `TypeIfNotUnknown` will be false. + +@example +``` +import type {IfUnknown} from 'type-fest'; + +``` + +@category Utilities +*/ +export type IfUnknown = ( + IsUnknown extends true ? TypeIfUnknown : TypeIfNotUnknown +); diff --git a/source/jsonify.d.ts b/source/jsonify.d.ts index 1dc8780df..42346fdb7 100644 --- a/source/jsonify.d.ts +++ b/source/jsonify.d.ts @@ -1,8 +1,9 @@ import type {JsonPrimitive, JsonValue} from './basic'; import type {EmptyObject} from './empty-object'; -import type {IsAny, UndefinedToOptional} from './internal'; +import type {UndefinedToOptional} from './internal'; import type {NegativeInfinity, PositiveInfinity} from './numeric'; import type {TypedArray} from './typed-array'; +import type {IsAny} from './is-if-any'; // Note: The return value has to be `any` and not `unknown` so it can match `void`. type NotJsonable = ((...arguments_: any[]) => any) | undefined | symbol; diff --git a/source/set-return-type.d.ts b/source/set-return-type.d.ts index 8adc46a5f..7c770bbd8 100644 --- a/source/set-return-type.d.ts +++ b/source/set-return-type.d.ts @@ -1,4 +1,4 @@ -import type {IsUnknown} from './internal'; +import type {IsUnknown} from './is-if-unknown'; /** Create a function type with a return type of your choice and the same parameters as the given function type. diff --git a/test-d/is-if-any.ts b/test-d/is-if-any.ts new file mode 100644 index 000000000..2b50acdaf --- /dev/null +++ b/test-d/is-if-any.ts @@ -0,0 +1,27 @@ +import {expectError, expectType} from 'tsd'; +import type {IsAny, IfAny} from '../index'; + +declare const anything: any; +declare const something = 'something'; + +// `IsAny` should only be true for `any` +expectType>(true); +expectType>(true); +expectType>(false); +expectType>(false); +expectType>(false); +expectType>(false); +expectType>(false); +expectType>(false); + +// `IfAny` should return `true`/`false` if only `T` is specified +expectType>(true); +expectType>(true); +expectType>(false); +expectType>('F'); + +// Missing generic parameter +expectError(anything); +// TODO: commented temporarily due to SamVerschueren/tsd#173 +// expectError(anything); + diff --git a/test-d/is-if-never.ts b/test-d/is-if-never.ts new file mode 100644 index 000000000..d862c018b --- /dev/null +++ b/test-d/is-if-never.ts @@ -0,0 +1,27 @@ +import {expectError, expectType} from 'tsd'; +import type {IsNever, IfNever} from '../index'; + +declare const _never: never; +declare const something = 'something'; + +// `IsNever` should only be true for `any` +expectType>(true); +expectType>(true); +expectType>(false); +expectType>(false); +expectType>(false); +expectType>(false); +expectType>(false); +expectType>(false); + +// `IfNever` should return `true`/`false` if only `T` is specified +expectType>(true); +expectType>('T'); +expectType>(false); +expectType>('F'); + +// Missing generic parameter +expectError(_never); +// TODO: commented temporarily due to SamVerschueren/tsd#173 +// expectError(_never); + diff --git a/test-d/is-if-unknown.ts b/test-d/is-if-unknown.ts new file mode 100644 index 000000000..3ebbb7992 --- /dev/null +++ b/test-d/is-if-unknown.ts @@ -0,0 +1,27 @@ +import {expectError, expectType} from 'tsd'; +import type {IsUnknown, IfUnknown} from '../index'; + +declare const _unknown: unknown; +declare const something = 'something'; + +// `IsUnknown` should only be true for `any` +expectType>(true); +expectType>(true); +expectType>(false); +expectType>(false); +expectType>(false); +expectType>(false); +expectType>(false); +expectType>(false); + +// `IfUnknown` should return `true`/`false` if only `T` is specified +expectType>(true); +expectType>('T'); +expectType>(false); +expectType>('F'); + +// Missing generic parameter +expectError(_unknown); +// TODO: commented temporarily due to SamVerschueren/tsd#173 +// expectError(_unknown); + From e146dfd1ed7849d3185c773d97f8e427a32aa402 Mon Sep 17 00:00:00 2001 From: Tommy Date: Sun, 5 Mar 2023 19:38:44 -0600 Subject: [PATCH 02/26] fix: `IfAny` test case --- test-d/is-if-any.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-d/is-if-any.ts b/test-d/is-if-any.ts index 2b50acdaf..7c75a9a69 100644 --- a/test-d/is-if-any.ts +++ b/test-d/is-if-any.ts @@ -16,7 +16,7 @@ expectType>(false); // `IfAny` should return `true`/`false` if only `T` is specified expectType>(true); -expectType>(true); +expectType>('T'); expectType>(false); expectType>('F'); From 3a5878e548796c6a546034cfedf9f42ec96f2850 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Mon, 6 Mar 2023 12:44:04 +0700 Subject: [PATCH 03/26] Update is-if-any.ts --- test-d/is-if-any.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test-d/is-if-any.ts b/test-d/is-if-any.ts index 7c75a9a69..2bab6fcd4 100644 --- a/test-d/is-if-any.ts +++ b/test-d/is-if-any.ts @@ -24,4 +24,3 @@ expectType>('F'); expectError(anything); // TODO: commented temporarily due to SamVerschueren/tsd#173 // expectError(anything); - From be4f89fd18dc99a75c2f488b4786f1be3019b625 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Mon, 6 Mar 2023 12:44:12 +0700 Subject: [PATCH 04/26] Update is-if-never.ts --- test-d/is-if-never.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test-d/is-if-never.ts b/test-d/is-if-never.ts index d862c018b..5798fe84c 100644 --- a/test-d/is-if-never.ts +++ b/test-d/is-if-never.ts @@ -24,4 +24,3 @@ expectType>('F'); expectError(_never); // TODO: commented temporarily due to SamVerschueren/tsd#173 // expectError(_never); - From 0a0fa77f2b81377c061e144be8d0891e968b5900 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Mon, 6 Mar 2023 12:44:19 +0700 Subject: [PATCH 05/26] Update is-if-unknown.ts --- test-d/is-if-unknown.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test-d/is-if-unknown.ts b/test-d/is-if-unknown.ts index 3ebbb7992..9f6cee723 100644 --- a/test-d/is-if-unknown.ts +++ b/test-d/is-if-unknown.ts @@ -24,4 +24,3 @@ expectType>('F'); expectError(_unknown); // TODO: commented temporarily due to SamVerschueren/tsd#173 // expectError(_unknown); - From 254b7a16163d516b1a7aa14a0301cff8bc982628 Mon Sep 17 00:00:00 2001 From: Tommy Date: Mon, 6 Mar 2023 00:21:47 -0600 Subject: [PATCH 06/26] chore: update `tsd`, uncomment test cases with `ts2707` error --- package.json | 2 +- test-d/is-if-any.ts | 3 +-- test-d/is-if-never.ts | 3 +-- test-d/is-if-unknown.ts | 3 +-- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 9a4db6f42..b7be948a5 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "devDependencies": { "@sindresorhus/tsconfig": "~0.7.0", "expect-type": "^0.15.0", - "tsd": "^0.24.1", + "tsd": "^0.26.0", "typescript": "^4.9.3", "xo": "^0.53.1" }, diff --git a/test-d/is-if-any.ts b/test-d/is-if-any.ts index 2bab6fcd4..eee20e552 100644 --- a/test-d/is-if-any.ts +++ b/test-d/is-if-any.ts @@ -22,5 +22,4 @@ expectType>('F'); // Missing generic parameter expectError(anything); -// TODO: commented temporarily due to SamVerschueren/tsd#173 -// expectError(anything); +expectError(anything); diff --git a/test-d/is-if-never.ts b/test-d/is-if-never.ts index 5798fe84c..af336bcb8 100644 --- a/test-d/is-if-never.ts +++ b/test-d/is-if-never.ts @@ -22,5 +22,4 @@ expectType>('F'); // Missing generic parameter expectError(_never); -// TODO: commented temporarily due to SamVerschueren/tsd#173 -// expectError(_never); +expectError(_never); diff --git a/test-d/is-if-unknown.ts b/test-d/is-if-unknown.ts index 9f6cee723..c2df090ec 100644 --- a/test-d/is-if-unknown.ts +++ b/test-d/is-if-unknown.ts @@ -22,5 +22,4 @@ expectType>('F'); // Missing generic parameter expectError(_unknown); -// TODO: commented temporarily due to SamVerschueren/tsd#173 -// expectError(_unknown); +expectError(_unknown); From 546af2a4a1dc848edca2e89ccf5d6f6ddb1b4ae4 Mon Sep 17 00:00:00 2001 From: Tommy Date: Mon, 6 Mar 2023 00:30:34 -0600 Subject: [PATCH 07/26] chore: separate into `is-x` and `if-x` files --- index.d.ts | 9 ++-- readme.md | 12 ++--- source/if-any.d.ts | 25 +++++++++ source/if-never.d.ts | 21 ++++++++ source/if-unknown.d.ts | 18 +++++++ source/internal.d.ts | 2 +- source/is-any.d.ts | 29 ++++++++++ source/is-if-any.d.ts | 53 ------------------- source/is-if-never.d.ts | 35 ------------ source/is-never.d.ts | 15 ++++++ .../{is-if-unknown.d.ts => is-unknown.d.ts} | 17 ------ source/jsonify.d.ts | 2 +- source/set-return-type.d.ts | 2 +- test-d/if-any.ts | 13 +++++ test-d/if-never.ts | 13 +++++ test-d/if-unknown.ts | 13 +++++ test-d/{is-if-any.ts => is-any.ts} | 9 +--- test-d/{is-if-never.ts => is-never.ts} | 9 +--- test-d/{is-if-unknown.ts => is-unknown.ts} | 9 +--- 19 files changed, 165 insertions(+), 141 deletions(-) create mode 100644 source/if-any.d.ts create mode 100644 source/if-never.d.ts create mode 100644 source/if-unknown.d.ts create mode 100644 source/is-any.d.ts delete mode 100644 source/is-if-any.d.ts delete mode 100644 source/is-if-never.d.ts create mode 100644 source/is-never.d.ts rename source/{is-if-unknown.d.ts => is-unknown.d.ts} (53%) create mode 100644 test-d/if-any.ts create mode 100644 test-d/if-never.ts create mode 100644 test-d/if-unknown.ts rename test-d/{is-if-any.ts => is-any.ts} (63%) rename test-d/{is-if-never.ts => is-never.ts} (63%) rename test-d/{is-if-unknown.ts => is-unknown.ts} (62%) diff --git a/index.d.ts b/index.d.ts index a5de93db9..00dd4786d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -76,9 +76,12 @@ 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 {IsAny, IfAny} from './source/is-if-any'; -export type {IsNever, IfNever} from './source/is-if-never'; -export type {IsUnknown, IfUnknown} from './source/is-if-unknown'; +export type {IsAny} from './source/is-any'; +export type {IfAny} from './source/if-any'; +export type {IsNever} from './source/is-never'; +export type {IfNever} from './source/if-never'; +export type {IsUnknown} from './source/is-unknown'; +export type {IfUnknown} from './source/if-unknown'; // Template literal types export type {CamelCase} from './source/camel-case'; diff --git a/readme.md b/readme.md index 0d732ac88..395ce4c47 100644 --- a/readme.md +++ b/readme.md @@ -171,12 +171,12 @@ 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. -- [`IsAny`](source/is-if-any.d.ts) - Returns a boolean for whether the given type is `any`. -- [`IfAny`](source/is-if-any.d.ts) - An `If`/`Else` like type that resolves whether the given type is `any`. -- [`IsNever`](source/is-if-never.d.ts) - Returns a boolean for whether the given type is `never`. -- [`IfNever`](source/is-if-never.d.ts) - An `If`/`Else` like type that resolves whether the given type is `never`. -- [`IsUnknown`](source/is-if-unknown.d.ts) - Returns a boolean for whether the given type is `unknown`. -- [`IfUnknown`](source/is-if-unknown.d.ts) - An `If`/`Else` like type that resolves whether the given type is `unknown`. +- [`IsAny`](source/is-any.d.ts) - Returns a boolean for whether the given type is `any`. +- [`IfAny`](source/if-any.d.ts) - An `If`/`Else` like type that resolves whether the given type is `any`. +- [`IsNever`](source/is-never.d.ts) - Returns a boolean for whether the given type is `never`. +- [`IfNever`](source/if-never.d.ts) - An `If`/`Else` like type that resolves whether the given type is `never`. +- [`IsUnknown`](source/is-unknown.d.ts) - Returns a boolean for whether the given type is `unknown`. +- [`IfUnknown`](source/if-unknown.d.ts) - An `If`/`Else` like type that resolves whether the given type is `unknown`. ### JSON diff --git a/source/if-any.d.ts b/source/if-any.d.ts new file mode 100644 index 000000000..5aed9e237 --- /dev/null +++ b/source/if-any.d.ts @@ -0,0 +1,25 @@ +import type {IsAny} from './is-any'; + +/** +If the given type `T` is `any`, the returned type is `TypeIfAny`. +Otherwise, the return type is `TypeIfNotAny`. +If only `T` is specified, `TypeIfAny` will be `true` and `TypeIfNotAny` will be false. + +@link https://stackoverflow.com/a/49928360/1490091 + +Useful for disallowing `any`s to be passed to a function or used in a type utility. + +@example +``` +import type {IfAny} from 'type-fest'; + +function get>, K extends keyof O = keyof O>(obj: O, key: K) { + return obj[key]; +} +``` + +@category Utilities +*/ +export type IfAny = ( + IsAny extends true ? TypeIfAny : TypeIfNotAny +); diff --git a/source/if-never.d.ts b/source/if-never.d.ts new file mode 100644 index 000000000..8c1e576a5 --- /dev/null +++ b/source/if-never.d.ts @@ -0,0 +1,21 @@ +import type {IsNever} from './is-never'; + +/** +If the given type `T` is `never`, the returned type is `TypeIfNever`. +Otherwise, the return type is `TypeIfNotNever`. +If only `T` is specified, `TypeIfNever` will be `true` and `TypeIfNotNever` will be false. + +@link https://github.com/microsoft/TypeScript/issues/31751#issuecomment-498526919 +@link https://stackoverflow.com/a/53984913/10292952 + +@example +``` +import type {IfNever} from 'type-fest'; + +``` + +@category Utilities +*/ +export type IfNever = ( + IsNever extends true ? TypeIfNever : TypeIfNotNever +); diff --git a/source/if-unknown.d.ts b/source/if-unknown.d.ts new file mode 100644 index 000000000..8c93a659d --- /dev/null +++ b/source/if-unknown.d.ts @@ -0,0 +1,18 @@ +import type {IsUnknown} from './is-unknown'; + +/** +If the given type `T` is `unknown`, the returned type is `TypeIfUnknown`. +Otherwise, the return type is `TypeIfNotUnknown`. +If only `T` is specified, `TypeIfUnknown` will be `true` and `TypeIfNotUnknown` will be false. + +@example +``` +import type {IfUnknown} from 'type-fest'; + +``` + +@category Utilities +*/ +export type IfUnknown = ( + IsUnknown extends true ? TypeIfUnknown : TypeIfNotUnknown +); diff --git a/source/internal.d.ts b/source/internal.d.ts index 049b19333..5d65312d0 100644 --- a/source/internal.d.ts +++ b/source/internal.d.ts @@ -1,7 +1,7 @@ import type {Primitive} from './primitive'; import type {Simplify} from './simplify'; import type {Trim} from './trim'; -import type {IsAny} from './is-if-any'; +import type {IsAny} from './is-any'; /** Infer the length of the given array ``. diff --git a/source/is-any.d.ts b/source/is-any.d.ts new file mode 100644 index 000000000..b09f28d5c --- /dev/null +++ b/source/is-any.d.ts @@ -0,0 +1,29 @@ +/** +Returns a boolean for whether the given type is `any`. + +@link https://stackoverflow.com/a/49928360/1490091 + +Useful for disallowing `any`s to be passed to a function or used in a type utility. + +@example +``` +import type {IsAny} from 'type-fest'; + +type Must = ( + IsAny extends false + ? IsNever extends false + ? IsUnknown extends false + ? IsUndefined extends false + ? IsNull extends false + ? T + : never + : never + : never + : never + : never +); +``` + +@category Utilities +*/ +export type IsAny = 0 extends 1 & T ? true : false; diff --git a/source/is-if-any.d.ts b/source/is-if-any.d.ts deleted file mode 100644 index 40c418adb..000000000 --- a/source/is-if-any.d.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** -Returns a boolean for whether the given type is `any`. - -@link https://stackoverflow.com/a/49928360/1490091 - -Useful for disallowing `any`s to be passed to a function or used in a type utility. - -@example -``` -import type {IsAny} from 'type-fest'; - -type Must = ( - IsAny extends false - ? IsNever extends false - ? IsUnknown extends false - ? IsUndefined extends false - ? IsNull extends false - ? T - : never - : never - : never - : never - : never -); -``` - -@category Utilities -*/ -export type IsAny = 0 extends 1 & T ? true : false; - -/** -If the given type `T` is `any`, the returned type is `TypeIfAny`. Otherwise, -the return type is `TypeIfNotAny`. If only `T` is specified, `TypeIfAny` -will be `true` and `TypeIfNotAny` will be false. - -@link https://stackoverflow.com/a/49928360/1490091 - -Useful for disallowing `any`s to be passed to a function or used in a type utility. - -@example -``` -import type {IfAny} from 'type-fest'; - -function get>, K extends keyof O = keyof O>(obj: O, key: K) { - return obj[key]; -} -``` - -@category Utilities -*/ -export type IfAny = ( - IsAny extends true ? TypeIfAny : TypeIfNotAny -); diff --git a/source/is-if-never.d.ts b/source/is-if-never.d.ts deleted file mode 100644 index 2a92ccf7d..000000000 --- a/source/is-if-never.d.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** -Returns a boolean for whether the given type is `never`. - -@link https://github.com/microsoft/TypeScript/issues/31751#issuecomment-498526919 -@link https://stackoverflow.com/a/53984913/10292952 - -@example -``` -import type {IsNever} from 'type-fest'; - -``` - -@category Utilities -*/ -export type IsNever = [T] extends [never] ? true : false; - -/** -If the given type `T` is `never`, the returned type is `TypeIfNever`. Otherwise, -the return type is `TypeIfNotNever`. If only `T` is specified, `TypeIfNever` -will be `true` and `TypeIfNotNever` will be false. - -@link https://github.com/microsoft/TypeScript/issues/31751#issuecomment-498526919 -@link https://stackoverflow.com/a/53984913/10292952 - -@example -``` -import type {IfNever} from 'type-fest'; - -``` - -@category Utilities -*/ -export type IfNever = ( - IsNever extends true ? TypeIfNever : TypeIfNotNever -); diff --git a/source/is-never.d.ts b/source/is-never.d.ts new file mode 100644 index 000000000..268c480b1 --- /dev/null +++ b/source/is-never.d.ts @@ -0,0 +1,15 @@ +/** +Returns a boolean for whether the given type is `never`. + +@link https://github.com/microsoft/TypeScript/issues/31751#issuecomment-498526919 +@link https://stackoverflow.com/a/53984913/10292952 + +@example +``` +import type {IsNever} from 'type-fest'; + +``` + +@category Utilities +*/ +export type IsNever = [T] extends [never] ? true : false; diff --git a/source/is-if-unknown.d.ts b/source/is-unknown.d.ts similarity index 53% rename from source/is-if-unknown.d.ts rename to source/is-unknown.d.ts index ab3bc1466..afde4dea1 100644 --- a/source/is-if-unknown.d.ts +++ b/source/is-unknown.d.ts @@ -21,20 +21,3 @@ export type IsUnknown = ( : false : false ); - -/** -If the given type `T` is `unknown`, the returned type is `TypeIfUnknown`. -Otherwise, the return type is `TypeIfNotUnknown`. If only `T` is specified, -`TypeIfUnknown` will be `true` and `TypeIfNotUnknown` will be false. - -@example -``` -import type {IfUnknown} from 'type-fest'; - -``` - -@category Utilities -*/ -export type IfUnknown = ( - IsUnknown extends true ? TypeIfUnknown : TypeIfNotUnknown -); diff --git a/source/jsonify.d.ts b/source/jsonify.d.ts index 42346fdb7..a9039058a 100644 --- a/source/jsonify.d.ts +++ b/source/jsonify.d.ts @@ -3,7 +3,7 @@ import type {EmptyObject} from './empty-object'; import type {UndefinedToOptional} from './internal'; import type {NegativeInfinity, PositiveInfinity} from './numeric'; import type {TypedArray} from './typed-array'; -import type {IsAny} from './is-if-any'; +import type {IsAny} from './is-any'; // Note: The return value has to be `any` and not `unknown` so it can match `void`. type NotJsonable = ((...arguments_: any[]) => any) | undefined | symbol; diff --git a/source/set-return-type.d.ts b/source/set-return-type.d.ts index 7c770bbd8..3081a843e 100644 --- a/source/set-return-type.d.ts +++ b/source/set-return-type.d.ts @@ -1,4 +1,4 @@ -import type {IsUnknown} from './is-if-unknown'; +import type {IsUnknown} from './is-unknown'; /** Create a function type with a return type of your choice and the same parameters as the given function type. diff --git a/test-d/if-any.ts b/test-d/if-any.ts new file mode 100644 index 000000000..6de8b7b7b --- /dev/null +++ b/test-d/if-any.ts @@ -0,0 +1,13 @@ +import {expectError, expectType} from 'tsd'; +import type {IfAny} from '../index'; + +declare const anything: any; + +// `IfAny` should return `true`/`false` if only `T` is specified +expectType>(true); +expectType>('T'); +expectType>(false); +expectType>('F'); + +// Missing generic parameter +expectError(anything); diff --git a/test-d/if-never.ts b/test-d/if-never.ts new file mode 100644 index 000000000..05bd133cb --- /dev/null +++ b/test-d/if-never.ts @@ -0,0 +1,13 @@ +import {expectError, expectType} from 'tsd'; +import type {IfNever} from '../index'; + +declare const _never: never; + +// `IfNever` should return `true`/`false` if only `T` is specified +expectType>(true); +expectType>('T'); +expectType>(false); +expectType>('F'); + +// Missing generic parameter +expectError(_never); diff --git a/test-d/if-unknown.ts b/test-d/if-unknown.ts new file mode 100644 index 000000000..e4285da22 --- /dev/null +++ b/test-d/if-unknown.ts @@ -0,0 +1,13 @@ +import {expectError, expectType} from 'tsd'; +import type {IfUnknown} from '../index'; + +declare const _unknown: unknown; + +// `IfUnknown` should return `true`/`false` if only `T` is specified +expectType>(true); +expectType>('T'); +expectType>(false); +expectType>('F'); + +// Missing generic parameter +expectError(_unknown); diff --git a/test-d/is-if-any.ts b/test-d/is-any.ts similarity index 63% rename from test-d/is-if-any.ts rename to test-d/is-any.ts index eee20e552..006d8b5eb 100644 --- a/test-d/is-if-any.ts +++ b/test-d/is-any.ts @@ -1,5 +1,5 @@ import {expectError, expectType} from 'tsd'; -import type {IsAny, IfAny} from '../index'; +import type {IsAny} from '../index'; declare const anything: any; declare const something = 'something'; @@ -14,12 +14,5 @@ expectType>(false); expectType>(false); expectType>(false); -// `IfAny` should return `true`/`false` if only `T` is specified -expectType>(true); -expectType>('T'); -expectType>(false); -expectType>('F'); - // Missing generic parameter expectError(anything); -expectError(anything); diff --git a/test-d/is-if-never.ts b/test-d/is-never.ts similarity index 63% rename from test-d/is-if-never.ts rename to test-d/is-never.ts index af336bcb8..f9e8ab681 100644 --- a/test-d/is-if-never.ts +++ b/test-d/is-never.ts @@ -1,5 +1,5 @@ import {expectError, expectType} from 'tsd'; -import type {IsNever, IfNever} from '../index'; +import type {IsNever} from '../index'; declare const _never: never; declare const something = 'something'; @@ -14,12 +14,5 @@ expectType>(false); expectType>(false); expectType>(false); -// `IfNever` should return `true`/`false` if only `T` is specified -expectType>(true); -expectType>('T'); -expectType>(false); -expectType>('F'); - // Missing generic parameter expectError(_never); -expectError(_never); diff --git a/test-d/is-if-unknown.ts b/test-d/is-unknown.ts similarity index 62% rename from test-d/is-if-unknown.ts rename to test-d/is-unknown.ts index c2df090ec..aad4fb2d1 100644 --- a/test-d/is-if-unknown.ts +++ b/test-d/is-unknown.ts @@ -1,5 +1,5 @@ import {expectError, expectType} from 'tsd'; -import type {IsUnknown, IfUnknown} from '../index'; +import type {IsUnknown} from '../index'; declare const _unknown: unknown; declare const something = 'something'; @@ -14,12 +14,5 @@ expectType>(false); expectType>(false); expectType>(false); -// `IfUnknown` should return `true`/`false` if only `T` is specified -expectType>(true); -expectType>('T'); -expectType>(false); -expectType>('F'); - // Missing generic parameter expectError(_unknown); -expectError(_unknown); From 1431554c28b2c391f5384c0e0b63a2b701bd8d09 Mon Sep 17 00:00:00 2001 From: Tommy Date: Mon, 6 Mar 2023 00:32:06 -0600 Subject: [PATCH 08/26] fix: no hard wrapping --- source/if-any.d.ts | 4 +--- source/if-never.d.ts | 4 +--- source/if-unknown.d.ts | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/source/if-any.d.ts b/source/if-any.d.ts index 5aed9e237..165b33aa7 100644 --- a/source/if-any.d.ts +++ b/source/if-any.d.ts @@ -1,9 +1,7 @@ import type {IsAny} from './is-any'; /** -If the given type `T` is `any`, the returned type is `TypeIfAny`. -Otherwise, the return type is `TypeIfNotAny`. -If only `T` is specified, `TypeIfAny` will be `true` and `TypeIfNotAny` will be false. +If the given type `T` is `any`, the returned type is `TypeIfAny`. Otherwise, the return type is `TypeIfNotAny`. If only `T` is specified, `TypeIfAny` will be `true` and `TypeIfNotAny` will be false. @link https://stackoverflow.com/a/49928360/1490091 diff --git a/source/if-never.d.ts b/source/if-never.d.ts index 8c1e576a5..3751f3444 100644 --- a/source/if-never.d.ts +++ b/source/if-never.d.ts @@ -1,9 +1,7 @@ import type {IsNever} from './is-never'; /** -If the given type `T` is `never`, the returned type is `TypeIfNever`. -Otherwise, the return type is `TypeIfNotNever`. -If only `T` is specified, `TypeIfNever` will be `true` and `TypeIfNotNever` will be false. +If the given type `T` is `never`, the returned type is `TypeIfNever`. Otherwise, the return type is `TypeIfNotNever`. If only `T` is specified, `TypeIfNever` will be `true` and `TypeIfNotNever` will be false. @link https://github.com/microsoft/TypeScript/issues/31751#issuecomment-498526919 @link https://stackoverflow.com/a/53984913/10292952 diff --git a/source/if-unknown.d.ts b/source/if-unknown.d.ts index 8c93a659d..90f7adbf5 100644 --- a/source/if-unknown.d.ts +++ b/source/if-unknown.d.ts @@ -1,9 +1,7 @@ import type {IsUnknown} from './is-unknown'; /** -If the given type `T` is `unknown`, the returned type is `TypeIfUnknown`. -Otherwise, the return type is `TypeIfNotUnknown`. -If only `T` is specified, `TypeIfUnknown` will be `true` and `TypeIfNotUnknown` will be false. +If the given type `T` is `unknown`, the returned type is `TypeIfUnknown`. Otherwise, the return type is `TypeIfNotUnknown`. If only `T` is specified, `TypeIfUnknown` will be `true` and `TypeIfNotUnknown` will be false. @example ``` From 10362554d3807ead6150c11b34ad91d3c23be0f5 Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Mon, 6 Mar 2023 19:53:01 -0600 Subject: [PATCH 09/26] feat(`any`): add documentation --- source/if-any.d.ts | 11 ++++++++++- source/is-any.d.ts | 25 ++++++++++++------------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/source/if-any.d.ts b/source/if-any.d.ts index 165b33aa7..944a48e3d 100644 --- a/source/if-any.d.ts +++ b/source/if-any.d.ts @@ -11,9 +11,18 @@ Useful for disallowing `any`s to be passed to a function or used in a type utili ``` import type {IfAny} from 'type-fest'; -function get>, K extends keyof O = keyof O>(obj: O, key: K) { +const typedObject = {a: 1, b: 2} as const; +const anyObject: any = {a: 1, b: 2}; + +function get>, K extends keyof O = keyof O>(obj: O, key: K) { return obj[key]; } + +const typedA = get(typedObject, 'a'); +//=> 1 + +const anyA = get(anyObject, 'a'); +//=> any ``` @category Utilities diff --git a/source/is-any.d.ts b/source/is-any.d.ts index b09f28d5c..942db66e0 100644 --- a/source/is-any.d.ts +++ b/source/is-any.d.ts @@ -9,19 +9,18 @@ Useful for disallowing `any`s to be passed to a function or used in a type utili ``` import type {IsAny} from 'type-fest'; -type Must = ( - IsAny extends false - ? IsNever extends false - ? IsUnknown extends false - ? IsUndefined extends false - ? IsNull extends false - ? T - : never - : never - : never - : never - : never -); +const typedObject = {a: 1, b: 2} as const; +const anyObject: any = {a: 1, b: 2}; + +function get extends true ? {} : Record), K extends keyof O = keyof O>(obj: O, key: K) { + return obj[key]; +} + +const typedA = get(typedObject, 'a'); +//=> 1 + +const anyA = get(anyObject, 'a'); +//=> any ``` @category Utilities From 6a39a2988dd4b81348853588fd34e091994d7e18 Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Tue, 7 Mar 2023 20:17:43 -0600 Subject: [PATCH 10/26] feat(`never`): add documentation --- source/if-never.d.ts | 32 ++++++++++++++++++++++++++++++++ source/is-never.d.ts | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/source/if-never.d.ts b/source/if-never.d.ts index 3751f3444..502295130 100644 --- a/source/if-never.d.ts +++ b/source/if-never.d.ts @@ -5,11 +5,43 @@ If the given type `T` is `never`, the returned type is `TypeIfNever`. Otherwise, @link https://github.com/microsoft/TypeScript/issues/31751#issuecomment-498526919 @link https://stackoverflow.com/a/53984913/10292952 +@link https://www.zhenghao.io/posts/ts-never + +Useful in type utilities, such as checking if something does not occur. @example ``` import type {IfNever} from 'type-fest'; +type And = + A extends true + ? B extends true + ? true + : false + : false; + +type AreStringsEqual = + And< + IfNever>, + IfNever> + >; + +type EndIfEqual = + AreStringsEqual extends true + ? never + : void; + +function endIfEqual(input: I, output: O): EndIfEqual { + if (input === output) { + process.exit(0); + } +} + +endIfEqual('abc', 'abc'); +//=> never + +endIfEqual('abc', '123'); +//=> void ``` @category Utilities diff --git a/source/is-never.d.ts b/source/is-never.d.ts index 268c480b1..66dbd2fc6 100644 --- a/source/is-never.d.ts +++ b/source/is-never.d.ts @@ -3,11 +3,43 @@ Returns a boolean for whether the given type is `never`. @link https://github.com/microsoft/TypeScript/issues/31751#issuecomment-498526919 @link https://stackoverflow.com/a/53984913/10292952 +@link https://www.zhenghao.io/posts/ts-never + +Useful in type utilities, such as checking if something does not occur. @example ``` import type {IsNever} from 'type-fest'; +type And = + A extends true + ? B extends true + ? true + : false + : false; + +type AreStringsEqual = + And< + IsNever> extends true ? true : false, + IsNever> extends true ? true : false + >; + +type EndIfEqual = + AreStringsEqual extends true + ? never + : void; + +function endIfEqual(input: I, output: O): EndIfEqual { + if (input === output) { + process.exit(0); + } +} + +endIfEqual('abc', 'abc'); +//=> never + +endIfEqual('abc', '123'); +//=> void ``` @category Utilities From 009f06dba457b5b567f0d6092709b7671d55e201 Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Tue, 7 Mar 2023 20:18:16 -0600 Subject: [PATCH 11/26] fix(`any`): unify documentation --- source/if-any.d.ts | 2 +- source/is-any.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/if-any.d.ts b/source/if-any.d.ts index 944a48e3d..ce39133d8 100644 --- a/source/if-any.d.ts +++ b/source/if-any.d.ts @@ -5,7 +5,7 @@ If the given type `T` is `any`, the returned type is `TypeIfAny`. Otherwise, the @link https://stackoverflow.com/a/49928360/1490091 -Useful for disallowing `any`s to be passed to a function or used in a type utility. +Useful in type utilities, such as disallowing `any`s to be passed to a function. @example ``` diff --git a/source/is-any.d.ts b/source/is-any.d.ts index 942db66e0..5c1841ab5 100644 --- a/source/is-any.d.ts +++ b/source/is-any.d.ts @@ -3,7 +3,7 @@ Returns a boolean for whether the given type is `any`. @link https://stackoverflow.com/a/49928360/1490091 -Useful for disallowing `any`s to be passed to a function or used in a type utility. +Useful in type utilities, such as disallowing `any`s to be passed to a function. @example ``` From e192e479ae620480e9237dace37c919dd33aa554 Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Tue, 7 Mar 2023 20:25:51 -0600 Subject: [PATCH 12/26] feat: move `IsNull` to `internal` --- source/internal.d.ts | 5 +++++ source/is-unknown.d.ts | 3 +-- test-d/internal.ts | 11 ++++++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/source/internal.d.ts b/source/internal.d.ts index 5d65312d0..5c35a37d2 100644 --- a/source/internal.d.ts +++ b/source/internal.d.ts @@ -241,3 +241,8 @@ export type HasMultipleCallSignatures unknown ? false : true : false; + +/** +Returns a boolean for whether the given type is `null`. +*/ +export type IsNull = [T] extends [null] ? true : false; diff --git a/source/is-unknown.d.ts b/source/is-unknown.d.ts index afde4dea1..4f9a8d6a4 100644 --- a/source/is-unknown.d.ts +++ b/source/is-unknown.d.ts @@ -1,5 +1,4 @@ -/** Returns a boolean for whether the given type is `null`. */ -type IsNull = [T] extends [null] ? true : false; +import type {IsNull} from './internal'; /** Returns a boolean for whether the given type is `unknown`. diff --git a/test-d/internal.ts b/test-d/internal.ts index ad7a44705..0a5700b09 100644 --- a/test-d/internal.ts +++ b/test-d/internal.ts @@ -1,5 +1,5 @@ import {expectType} from 'tsd'; -import type {IsWhitespace, IsNumeric} from '../source/internal'; +import type {IsWhitespace, IsNumeric, IsNull} from '../source/internal'; expectType>(false); expectType>(true); @@ -27,3 +27,12 @@ expectType>(false); expectType>(false); expectType>(false); expectType>(false); + +// https://www.typescriptlang.org/docs/handbook/type-compatibility.html +expectType>(true); +expectType>(true); +expectType>(true); +expectType>(false); // Depends on `strictNullChecks` +expectType>(false); +expectType>(false); +expectType>(false); From 11fb3025728bd54cec35f4cb342b305d433025d1 Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Tue, 7 Mar 2023 20:33:36 -0600 Subject: [PATCH 13/26] fix(`never`): add source of example --- source/if-never.d.ts | 1 + source/is-never.d.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/source/if-never.d.ts b/source/if-never.d.ts index 502295130..90d5b0230 100644 --- a/source/if-never.d.ts +++ b/source/if-never.d.ts @@ -20,6 +20,7 @@ type And = : false : false; +// https://github.com/andnp/SimplyTyped/blob/master/src/types/strings.ts type AreStringsEqual = And< IfNever>, diff --git a/source/is-never.d.ts b/source/is-never.d.ts index 66dbd2fc6..ee1f4b00f 100644 --- a/source/is-never.d.ts +++ b/source/is-never.d.ts @@ -18,6 +18,7 @@ type And = : false : false; +// https://github.com/andnp/SimplyTyped/blob/master/src/types/strings.ts type AreStringsEqual = And< IsNever> extends true ? true : false, From 43f34f6ce786983aaaf25a4c5cfbbf917357a513 Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Tue, 7 Mar 2023 20:42:42 -0600 Subject: [PATCH 14/26] chore: add tests for `void` --- test-d/is-any.ts | 1 + test-d/is-never.ts | 1 + test-d/is-unknown.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/test-d/is-any.ts b/test-d/is-any.ts index 006d8b5eb..97ca8e17f 100644 --- a/test-d/is-any.ts +++ b/test-d/is-any.ts @@ -13,6 +13,7 @@ expectType>(false); expectType>(false); expectType>(false); expectType>(false); +expectType>(false); // Missing generic parameter expectError(anything); diff --git a/test-d/is-never.ts b/test-d/is-never.ts index f9e8ab681..76e7e5c6a 100644 --- a/test-d/is-never.ts +++ b/test-d/is-never.ts @@ -13,6 +13,7 @@ expectType>(false); expectType>(false); expectType>(false); expectType>(false); +expectType>(false); // Missing generic parameter expectError(_never); diff --git a/test-d/is-unknown.ts b/test-d/is-unknown.ts index aad4fb2d1..6ae78589c 100644 --- a/test-d/is-unknown.ts +++ b/test-d/is-unknown.ts @@ -13,6 +13,7 @@ expectType>(false); expectType>(false); expectType>(false); expectType>(false); +expectType>(false); // Missing generic parameter expectError(_unknown); From 1530ef9d1842bf3805b31fd74c1c5d532d6c3337 Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Tue, 7 Mar 2023 21:38:01 -0600 Subject: [PATCH 15/26] feat(`unknown`): add documentation --- source/if-unknown.d.ts | 31 +++++++++++++++++++++++++++++++ source/is-unknown.d.ts | 30 ++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/source/if-unknown.d.ts b/source/if-unknown.d.ts index 90f7adbf5..fd3b7fd88 100644 --- a/source/if-unknown.d.ts +++ b/source/if-unknown.d.ts @@ -3,10 +3,41 @@ import type {IsUnknown} from './is-unknown'; /** If the given type `T` is `unknown`, the returned type is `TypeIfUnknown`. Otherwise, the return type is `TypeIfNotUnknown`. If only `T` is specified, `TypeIfUnknown` will be `true` and `TypeIfNotUnknown` will be false. +Useful in type utilities, such as when dealing with unknown data from API calls. + @example ``` import type {IfUnknown} from 'type-fest'; +// https://github.com/pajecawav/tiny-global-store/blob/master/src/index.ts +type Action = IfUnknown< + TPayload, + (state: TState) => TState, + (state: TState, payload: TPayload) => TState +>; + +class Store { + constructor(private state: TState) {} + + execute(action: Action, payload?: TPayload): TState { + this.state = action(this.state, payload); + return this.state; + } + + // ... other methods +} + +const store = new Store({value: 1}); +declare const someExternalData: unknown; + +store.execute(state => ({value: state.value + 1})); +//=> `TPayload` is `void` + +store.execute((state, payload) => ({value: state.value + payload}), 5); +//=> `TPayload` is `5` + +store.execute((state, payload) => ({value: state.value + payload}), someExternalData); +//=> Errors: `action` is `(state: TState) => TState` ``` @category Utilities diff --git a/source/is-unknown.d.ts b/source/is-unknown.d.ts index 4f9a8d6a4..eef4afd4e 100644 --- a/source/is-unknown.d.ts +++ b/source/is-unknown.d.ts @@ -5,10 +5,40 @@ Returns a boolean for whether the given type is `unknown`. @link https://github.com/dsherret/conditional-type-checks/pull/16 +Useful in type utilities, such as when dealing with unknown data from API calls. + @example ``` import type {IsUnknown} from 'type-fest'; +// https://github.com/pajecawav/tiny-global-store/blob/master/src/index.ts +type Action = + IsUnknown extends true + ? (state: TState) => TState, + : (state: TState, payload: TPayload) => TState; + +class Store { + constructor(private state: TState) {} + + execute(action: Action, payload?: TPayload): TState { + this.state = action(this.state, payload); + return this.state; + } + + // ... other methods +} + +const store = new Store({value: 1}); +declare const someExternalData: unknown; + +store.execute(state => ({value: state.value + 1})); +//=> `TPayload` is `void` + +store.execute((state, payload) => ({value: state.value + payload}), 5); +//=> `TPayload` is `5` + +store.execute((state, payload) => ({value: state.value + payload}), someExternalData); +//=> Errors: `action` is `(state: TState) => TState` ``` @category Utilities From b391d449e2abf48a26f0a165acd449f78ba39d4f Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sat, 11 Mar 2023 03:13:30 +0700 Subject: [PATCH 16/26] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b7be948a5..801f7a2c9 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "devDependencies": { "@sindresorhus/tsconfig": "~0.7.0", "expect-type": "^0.15.0", - "tsd": "^0.26.0", + "tsd": "^0.27.0", "typescript": "^4.9.3", "xo": "^0.53.1" }, From d4e10d1b13ae775be12f0d8fb349606151d1a61e Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Fri, 10 Mar 2023 15:09:35 -0600 Subject: [PATCH 17/26] chore(`is-literal`): update `IsNever` import --- source/is-literal.d.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/is-literal.d.ts b/source/is-literal.d.ts index ef458cab1..18e3108c7 100644 --- a/source/is-literal.d.ts +++ b/source/is-literal.d.ts @@ -1,6 +1,7 @@ import type {Primitive} from './primitive'; import type {Numeric} from './numeric'; -import type {IsNever, IsNotFalse} from './internal'; +import type {IsNotFalse} from './internal'; +import type {IsNever} from './is-never'; /** Returns a boolean for whether the given type `T` is the specified `LiteralType`. From eee302c423bb8550c0d552f9e6fb217e4fb37547 Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Tue, 14 Mar 2023 15:16:50 -0500 Subject: [PATCH 18/26] chore: set primary category as `Type Guard` --- source/if-any.d.ts | 1 + source/if-never.d.ts | 1 + source/if-unknown.d.ts | 1 + source/is-any.d.ts | 1 + source/is-equal.d.ts | 1 + source/is-literal.d.ts | 10 +++++----- source/is-never.d.ts | 1 + 7 files changed, 11 insertions(+), 5 deletions(-) diff --git a/source/if-any.d.ts b/source/if-any.d.ts index ce39133d8..cffc2a2c8 100644 --- a/source/if-any.d.ts +++ b/source/if-any.d.ts @@ -25,6 +25,7 @@ const anyA = get(anyObject, 'a'); //=> any ``` +@category Type Guard @category Utilities */ export type IfAny = ( diff --git a/source/if-never.d.ts b/source/if-never.d.ts index 90d5b0230..fb27047e2 100644 --- a/source/if-never.d.ts +++ b/source/if-never.d.ts @@ -45,6 +45,7 @@ endIfEqual('abc', '123'); //=> void ``` +@category Type Guard @category Utilities */ export type IfNever = ( diff --git a/source/if-unknown.d.ts b/source/if-unknown.d.ts index fd3b7fd88..df530d6e1 100644 --- a/source/if-unknown.d.ts +++ b/source/if-unknown.d.ts @@ -40,6 +40,7 @@ store.execute((state, payload) => ({value: state.value + payload}), someExternal //=> Errors: `action` is `(state: TState) => TState` ``` +@category Type Guard @category Utilities */ export type IfUnknown = ( diff --git a/source/is-any.d.ts b/source/is-any.d.ts index 5c1841ab5..9f1c3ec35 100644 --- a/source/is-any.d.ts +++ b/source/is-any.d.ts @@ -23,6 +23,7 @@ const anyA = get(anyObject, 'a'); //=> any ``` +@category Type Guard @category Utilities */ export type IsAny = 0 extends 1 & T ? true : false; diff --git a/source/is-equal.d.ts b/source/is-equal.d.ts index 4a64dda57..5a8a39ba4 100644 --- a/source/is-equal.d.ts +++ b/source/is-equal.d.ts @@ -21,6 +21,7 @@ type Includes = : false; ``` +@category Type Guard @category Utilities */ export type IsEqual = diff --git a/source/is-literal.d.ts b/source/is-literal.d.ts index 18e3108c7..a9e4c68b7 100644 --- a/source/is-literal.d.ts +++ b/source/is-literal.d.ts @@ -78,8 +78,8 @@ const output = capitalize('hello, world!'); //=> 'Hello, world!' ``` -@category Utilities @category Type Guard +@category Utilities */ export type IsStringLiteral = LiteralCheck; @@ -126,8 +126,8 @@ endsWith('abc123', end); //=> boolean ``` -@category Utilities @category Type Guard +@category Utilities */ export type IsNumericLiteral = LiteralChecks; @@ -166,8 +166,8 @@ const eitherId = getId({asString: runtimeBoolean}); //=> number | string ``` -@category Utilities @category Type Guard +@category Utilities */ export type IsBooleanLiteral = LiteralCheck; @@ -201,8 +201,8 @@ get({[symbolValue]: 1} as const, symbolValue); //=> number ``` -@category Utilities @category Type Guard +@category Utilities */ export type IsSymbolLiteral = LiteralCheck; @@ -247,7 +247,7 @@ stripLeading(str, 'abc'); //=> string ``` -@category Utilities @category Type Guard +@category Utilities */ export type IsLiteral = IsNotFalse>; diff --git a/source/is-never.d.ts b/source/is-never.d.ts index ee1f4b00f..740ddc434 100644 --- a/source/is-never.d.ts +++ b/source/is-never.d.ts @@ -43,6 +43,7 @@ endIfEqual('abc', '123'); //=> void ``` +@category Type Guard @category Utilities */ export type IsNever = [T] extends [never] ? true : false; From 853cfbf7402c6ef718adece1cb7adc936a9fbbb1 Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Tue, 14 Mar 2023 15:17:12 -0500 Subject: [PATCH 19/26] docs: reorganize readme and add note for `IsT`/`IfT` types --- readme.md | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/readme.md b/readme.md index 1597a9e5d..f3855df4d 100644 --- a/readme.md +++ b/readme.md @@ -171,17 +171,42 @@ 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. + +### Type Guard + +#### `IsType` vs. `IfType` + +For every `IsT` type (e.g. `IsAny`), there is an associated `IfT` type that can help simplify conditional types. While the `IsT` types return a `boolean`, the `IfT` types act like an `If`/`Else` - they resolve to the given `TypeIfT` or `TypeIfNotT` depending on whether `IsX` is `true` or not. By default, `IfT` returns a `boolean`: + +```ts +type IfAny = ( + IsAny extends true ? TypeIfAny : TypeIfNotAny +); +``` + +#### Usage + +```ts +import type {IsAny, IfAny} from 'type-fest'; + +type ShouldBeTrue = IsAny extends true ? true : false; +//=> true + +type ShouldBeFalse = IfAny<'not any'>; +//=> false + +type ShouldBeNever = IfAny<'not any', 'not never', never>; +//=> never +``` + - [`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). -- [`IsAny`](source/is-any.d.ts) - Returns a boolean for whether the given type is `any`. -- [`IfAny`](source/if-any.d.ts) - An `If`/`Else` like type that resolves whether the given type is `any`. -- [`IsNever`](source/is-never.d.ts) - Returns a boolean for whether the given type is `never`. -- [`IfNever`](source/if-never.d.ts) - An `If`/`Else` like type that resolves whether the given type is `never`. -- [`IsUnknown`](source/is-unknown.d.ts) - Returns a boolean for whether the given type is `unknown`. -- [`IfUnknown`](source/if-unknown.d.ts) - An `If`/`Else` like type that resolves whether the given type is `unknown`. +- [`IsAny`](source/is-any.d.ts) - Returns a boolean for whether the given type is `any`. (Alternate: `IfAny`.) +- [`IsNever`](source/is-never.d.ts) - Returns a boolean for whether the given type is `never`. (Alternate: `IfNever`.) +- [`IsUnknown`](source/is-unknown.d.ts) - Returns a boolean for whether the given type is `unknown`. (Alternate: `IfUnknown`.) ### JSON From dc5ba9c2f26577e980de955a8579dcd8a6c5955a Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Thu, 16 Mar 2023 16:42:12 -0500 Subject: [PATCH 20/26] docs(`IfAny`): update description and example --- readme.md | 4 ++-- source/if-any.d.ts | 24 ++++++++++-------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/readme.md b/readme.md index 698e931fe..4f8725046 100644 --- a/readme.md +++ b/readme.md @@ -198,8 +198,8 @@ type ShouldBeTrue = IsAny extends true ? true : false; type ShouldBeFalse = IfAny<'not any'>; //=> false -type ShouldBeNever = IfAny<'not any', 'not never', never>; -//=> never +type ShouldBeNever = IfAny<'not any', 'not never', 'never'>; +//=> 'never' ``` - [`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). diff --git a/source/if-any.d.ts b/source/if-any.d.ts index cffc2a2c8..b1e6bcc17 100644 --- a/source/if-any.d.ts +++ b/source/if-any.d.ts @@ -1,28 +1,24 @@ import type {IsAny} from './is-any'; /** -If the given type `T` is `any`, the returned type is `TypeIfAny`. Otherwise, the return type is `TypeIfNotAny`. If only `T` is specified, `TypeIfAny` will be `true` and `TypeIfNotAny` will be false. +An `If`/`Else`-like type that resolves depending on whether the given type is `any`. -@link https://stackoverflow.com/a/49928360/1490091 +If the given type `T` is `any`, the returned type is `TypeIfAny`. Otherwise, the return type is `TypeIfNotAny`. If only `T` is specified, `TypeIfAny` will be `true` and `TypeIfNotAny` will be false. -Useful in type utilities, such as disallowing `any`s to be passed to a function. +@see IsAny @example ``` -import type {IfAny} from 'type-fest'; - -const typedObject = {a: 1, b: 2} as const; -const anyObject: any = {a: 1, b: 2}; +import type {IsAny, IfAny} from 'type-fest'; -function get>, K extends keyof O = keyof O>(obj: O, key: K) { - return obj[key]; -} +type ShouldBeTrue = IsAny extends true ? true : false; +//=> true -const typedA = get(typedObject, 'a'); -//=> 1 +type ShouldBeFalse = IfAny<'not any'>; +//=> false -const anyA = get(anyObject, 'a'); -//=> any +type ShouldBeNever = IfAny<'not any', 'not never', 'never'>; +//=> 'never' ``` @category Type Guard From 28bbfae76424aa7e778018c5d4a4a3db324d33ab Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Thu, 16 Mar 2023 16:50:53 -0500 Subject: [PATCH 21/26] chore(`IfAny`): consistent variable name --- test-d/if-any.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-d/if-any.ts b/test-d/if-any.ts index 6de8b7b7b..d31f29667 100644 --- a/test-d/if-any.ts +++ b/test-d/if-any.ts @@ -1,7 +1,7 @@ import {expectError, expectType} from 'tsd'; import type {IfAny} from '../index'; -declare const anything: any; +declare const _any: any; // `IfAny` should return `true`/`false` if only `T` is specified expectType>(true); @@ -10,4 +10,4 @@ expectType>(false); expectType>('F'); // Missing generic parameter -expectError(anything); +expectError(_any); From 81d38ae3b6952d228c3e0c5bfa466653d432f113 Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Thu, 16 Mar 2023 17:09:26 -0500 Subject: [PATCH 22/26] chore: consistent test ordering --- test-d/if-any.ts | 2 +- test-d/if-never.ts | 2 +- test-d/if-unknown.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test-d/if-any.ts b/test-d/if-any.ts index d31f29667..972cdbc60 100644 --- a/test-d/if-any.ts +++ b/test-d/if-any.ts @@ -5,8 +5,8 @@ declare const _any: any; // `IfAny` should return `true`/`false` if only `T` is specified expectType>(true); -expectType>('T'); expectType>(false); +expectType>('T'); expectType>('F'); // Missing generic parameter diff --git a/test-d/if-never.ts b/test-d/if-never.ts index 05bd133cb..724d9d831 100644 --- a/test-d/if-never.ts +++ b/test-d/if-never.ts @@ -5,8 +5,8 @@ declare const _never: never; // `IfNever` should return `true`/`false` if only `T` is specified expectType>(true); -expectType>('T'); expectType>(false); +expectType>('T'); expectType>('F'); // Missing generic parameter diff --git a/test-d/if-unknown.ts b/test-d/if-unknown.ts index e4285da22..a11a5ce6f 100644 --- a/test-d/if-unknown.ts +++ b/test-d/if-unknown.ts @@ -5,8 +5,8 @@ declare const _unknown: unknown; // `IfUnknown` should return `true`/`false` if only `T` is specified expectType>(true); -expectType>('T'); expectType>(false); +expectType>('T'); expectType>('F'); // Missing generic parameter From d55804c29595b50dcba97086b8ffe5ccc85bdec3 Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Mon, 27 Mar 2023 11:59:59 -0500 Subject: [PATCH 23/26] chore: merge changes from main --- package.json | 11 ++++++++--- source/except.d.ts | 33 ++++++++++++++++++++++++++++----- source/numeric.d.ts | 2 +- test-d/abstract-class.ts | 8 ++++---- test-d/except.ts | 18 ++++++++++++++++-- test-d/is-literal.ts | 4 ++-- 6 files changed, 59 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 801f7a2c9..2fa54086a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "type-fest", - "version": "3.6.1", + "version": "3.7.1", "description": "A collection of essential TypeScript types", "license": "(MIT OR CC0-1.0)", "repository": "sindresorhus/type-fest", @@ -36,8 +36,8 @@ "devDependencies": { "@sindresorhus/tsconfig": "~0.7.0", "expect-type": "^0.15.0", - "tsd": "^0.27.0", - "typescript": "^4.9.3", + "tsd": "^0.28.0", + "typescript": "^5.0.2", "xo": "^0.53.1" }, "xo": { @@ -49,5 +49,10 @@ "@typescript-eslint/no-redeclare": "off", "@typescript-eslint/no-confusing-void-expression": "off" } + }, + "tsd": { + "compilerOptions": { + "noUnusedLocals": false + } } } diff --git a/source/except.d.ts b/source/except.d.ts index bf1dd4154..7abf3027c 100644 --- a/source/except.d.ts +++ b/source/except.d.ts @@ -29,9 +29,22 @@ type Filtered = Filter<'bar', 'foo'>; */ type Filter = IsEqual extends true ? never : (KeyType extends ExcludeType ? never : KeyType); +type ExceptOptions = { + /** + Disallow assigning non-specified properties. + + Note that any omitted properties in the resulting type will be present in autocomplete as `undefined`. + + @default false + */ + requireExactProps?: boolean; +}; + /** Create a type from an object type without certain keys. +We recommend setting the `requireExactProps` option to `true`. + This type is a stricter version of [`Omit`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html#the-omit-helper-type). The `Omit` type does not restrict the omitted keys to be keys present on the given type, while `Except` does. The benefits of a stricter type are avoiding typos and allowing the compiler to pick up on rename refactors automatically. This type was proposed to the TypeScript team, which declined it, saying they prefer that libraries implement stricter versions of the built-in types ([microsoft/TypeScript#30825](https://github.com/microsoft/TypeScript/issues/30825#issuecomment-523668235)). @@ -43,15 +56,25 @@ import type {Except} from 'type-fest'; type Foo = { a: number; b: string; - c: boolean; }; -type FooWithoutA = Except; -//=> {b: string}; +type FooWithoutA = Except; +//=> {b: string} + +const fooWithoutA: FooWithoutA = {a: 1, b: '2'}; +//=> errors: 'a' does not exist in type '{ b: string; }' + +type FooWithoutB = Except; +//=> {a: number} & Partial> + +const fooWithoutB: FooWithoutB = {a: 1, b: '2'}; +//=> errors at 'b': Type 'string' is not assignable to type 'undefined'. ``` @category Object */ -export type Except = { +export type Except = { [KeyType in keyof ObjectType as Filter]: ObjectType[KeyType]; -}; +} & (Options['requireExactProps'] extends true + ? Partial> + : {}); diff --git a/source/numeric.d.ts b/source/numeric.d.ts index 614b61a14..2818ce394 100644 --- a/source/numeric.d.ts +++ b/source/numeric.d.ts @@ -1,4 +1,4 @@ -type Numeric = number | bigint; +export type Numeric = number | bigint; type Zero = 0 | 0n; diff --git a/test-d/abstract-class.ts b/test-d/abstract-class.ts index 7ae53bfcf..102e08df1 100644 --- a/test-d/abstract-class.ts +++ b/test-d/abstract-class.ts @@ -1,4 +1,4 @@ -import {expectAssignable, expectError} from 'tsd'; +import {expectError} from 'tsd'; import type {AbstractConstructor, AbstractClass} from '../index'; abstract class Foo { @@ -31,10 +31,10 @@ function assertWithBar() { // eslint-disable-next-line @typescript-eslint/no-empty-function barMethod() {} } - expectAssignable(functionRecevingAbsClass(withBar(Bar))); - expectAssignable(functionRecevingAbsClass(CorrectConcreteExtendedBar)); + functionRecevingAbsClass(withBar(Bar)); + functionRecevingAbsClass(CorrectConcreteExtendedBar); } -expectAssignable(functionRecevingAbsClass(Foo)); +functionRecevingAbsClass(Foo); expectError(functionRecevingAbsClass(Foo)); assertWithBar(); diff --git a/test-d/except.ts b/test-d/except.ts index c166e52f8..b593137a8 100644 --- a/test-d/except.ts +++ b/test-d/except.ts @@ -1,8 +1,22 @@ -import {expectType} from 'tsd'; +import {expectType, expectError} from 'tsd'; import type {Except} from '../index'; declare const except: Except<{a: number; b: string}, 'b'>; expectType<{a: number}>(except); +expectError(except.b); + +const nonStrict = { + a: 1, + b: '2', +}; + +const nonStrictAssignment: typeof except = nonStrict; // No error + +declare const strictExcept: Except<{a: number; b: string}, 'b', {requireExactProps: true}>; + +expectError(() => { + const strictAssignment: typeof strictExcept = nonStrict; +}); // Generic properties type Example = { @@ -11,7 +25,7 @@ type Example = { bar: string; }; -const test: Except = {foo: 123, bar: 'asdf'}; +const test: Except = {foo: 123, bar: 'asdf'}; expectType(test.foo); // eslint-disable-next-line @typescript-eslint/dot-notation expectType(test['bar']); diff --git a/test-d/is-literal.ts b/test-d/is-literal.ts index 0ee48a819..d10fb0239 100644 --- a/test-d/is-literal.ts +++ b/test-d/is-literal.ts @@ -9,8 +9,8 @@ import type { const stringLiteral = ''; const numberLiteral = 1; -// @ts-expect-error: suppress BigInt literal tsd warning -const bigintLiteral = 1n; +// Note: tsd warns on direct literal usage so we cast to the literal type +const bigintLiteral = BigInt(1) as 1n; const booleanLiteral = true; const symbolLiteral = Symbol(''); From af4196a1d6303cde48db69cc5a74354fcc86e7aa Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Mon, 27 Mar 2023 12:03:56 -0500 Subject: [PATCH 24/26] docs: rename `alternate` to `conditional` --- readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index 4f8725046..394fe15c5 100644 --- a/readme.md +++ b/readme.md @@ -207,9 +207,9 @@ type ShouldBeNever = IfAny<'not any', 'not never', 'never'>; - [`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). -- [`IsAny`](source/is-any.d.ts) - Returns a boolean for whether the given type is `any`. (Alternate: `IfAny`.) -- [`IsNever`](source/is-never.d.ts) - Returns a boolean for whether the given type is `never`. (Alternate: `IfNever`.) -- [`IsUnknown`](source/is-unknown.d.ts) - Returns a boolean for whether the given type is `unknown`. (Alternate: `IfUnknown`.) +- [`IsAny`](source/is-any.d.ts) - Returns a boolean for whether the given type is `any`. (Conditional version: [`IfAny`](source/if-any.d.ts).) +- [`IsNever`](source/is-never.d.ts) - Returns a boolean for whether the given type is `never`. (Conditional version: [`IfNever`](source/if-never.d.ts).) +- [`IsUnknown`](source/is-unknown.d.ts) - Returns a boolean for whether the given type is `unknown`. (Conditional version: [`IfUnknown`](source/if-unknown.d.ts).) ### JSON From f61b07deba5f7c6d11c303905426eb55f12aa0dd Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Mon, 27 Mar 2023 12:10:28 -0500 Subject: [PATCH 25/26] chore(`IfT`): improve descriptions and links --- source/if-any.d.ts | 6 ++---- source/if-never.d.ts | 8 ++------ source/if-unknown.d.ts | 4 ++-- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/source/if-any.d.ts b/source/if-any.d.ts index b1e6bcc17..fec133d08 100644 --- a/source/if-any.d.ts +++ b/source/if-any.d.ts @@ -1,11 +1,9 @@ import type {IsAny} from './is-any'; /** -An `If`/`Else`-like type that resolves depending on whether the given type is `any`. +An if-else-like type that resolves depending on whether the given type is `any`. -If the given type `T` is `any`, the returned type is `TypeIfAny`. Otherwise, the return type is `TypeIfNotAny`. If only `T` is specified, `TypeIfAny` will be `true` and `TypeIfNotAny` will be false. - -@see IsAny +@see {@link IsAny} @example ``` diff --git a/source/if-never.d.ts b/source/if-never.d.ts index fb27047e2..814317533 100644 --- a/source/if-never.d.ts +++ b/source/if-never.d.ts @@ -1,13 +1,9 @@ import type {IsNever} from './is-never'; /** -If the given type `T` is `never`, the returned type is `TypeIfNever`. Otherwise, the return type is `TypeIfNotNever`. If only `T` is specified, `TypeIfNever` will be `true` and `TypeIfNotNever` will be false. +An if-else-like type that resolves depending on whether the given type is `never`. -@link https://github.com/microsoft/TypeScript/issues/31751#issuecomment-498526919 -@link https://stackoverflow.com/a/53984913/10292952 -@link https://www.zhenghao.io/posts/ts-never - -Useful in type utilities, such as checking if something does not occur. +@see {@link IsNever} @example ``` diff --git a/source/if-unknown.d.ts b/source/if-unknown.d.ts index df530d6e1..6526f1613 100644 --- a/source/if-unknown.d.ts +++ b/source/if-unknown.d.ts @@ -1,9 +1,9 @@ import type {IsUnknown} from './is-unknown'; /** -If the given type `T` is `unknown`, the returned type is `TypeIfUnknown`. Otherwise, the return type is `TypeIfNotUnknown`. If only `T` is specified, `TypeIfUnknown` will be `true` and `TypeIfNotUnknown` will be false. +An if-else-like type that resolves depending on whether the given type is `unknown`. -Useful in type utilities, such as when dealing with unknown data from API calls. +@see {@link IsUnknown} @example ``` From 64e5639c185227a232d477667a1e984b3cc51b33 Mon Sep 17 00:00:00 2001 From: tommy-mitchell Date: Mon, 27 Mar 2023 12:17:55 -0500 Subject: [PATCH 26/26] docs(`IfT`): simplify examples --- source/if-any.d.ts | 11 ++++------- source/if-never.d.ts | 33 ++++----------------------------- source/if-unknown.d.ts | 32 ++++---------------------------- 3 files changed, 12 insertions(+), 64 deletions(-) diff --git a/source/if-any.d.ts b/source/if-any.d.ts index fec133d08..1c17b76f6 100644 --- a/source/if-any.d.ts +++ b/source/if-any.d.ts @@ -7,16 +7,13 @@ An if-else-like type that resolves depending on whether the given type is `any`. @example ``` -import type {IsAny, IfAny} from 'type-fest'; +import type {IfAny} from 'type-fest'; -type ShouldBeTrue = IsAny extends true ? true : false; +type ShouldBeTrue = IfAny; //=> true -type ShouldBeFalse = IfAny<'not any'>; -//=> false - -type ShouldBeNever = IfAny<'not any', 'not never', 'never'>; -//=> 'never' +type ShouldBeBar = IfAny<'not any', 'foo', 'bar'>; +//=> 'bar' ``` @category Type Guard diff --git a/source/if-never.d.ts b/source/if-never.d.ts index 814317533..d33af2e8c 100644 --- a/source/if-never.d.ts +++ b/source/if-never.d.ts @@ -9,36 +9,11 @@ An if-else-like type that resolves depending on whether the given type is `never ``` import type {IfNever} from 'type-fest'; -type And = - A extends true - ? B extends true - ? true - : false - : false; +type ShouldBeTrue = IfNever; +//=> true -// https://github.com/andnp/SimplyTyped/blob/master/src/types/strings.ts -type AreStringsEqual = - And< - IfNever>, - IfNever> - >; - -type EndIfEqual = - AreStringsEqual extends true - ? never - : void; - -function endIfEqual(input: I, output: O): EndIfEqual { - if (input === output) { - process.exit(0); - } -} - -endIfEqual('abc', 'abc'); -//=> never - -endIfEqual('abc', '123'); -//=> void +type ShouldBeBar = IfNever<'not never', 'foo', 'bar'>; +//=> 'bar' ``` @category Type Guard diff --git a/source/if-unknown.d.ts b/source/if-unknown.d.ts index 6526f1613..828268c56 100644 --- a/source/if-unknown.d.ts +++ b/source/if-unknown.d.ts @@ -9,35 +9,11 @@ An if-else-like type that resolves depending on whether the given type is `unkno ``` import type {IfUnknown} from 'type-fest'; -// https://github.com/pajecawav/tiny-global-store/blob/master/src/index.ts -type Action = IfUnknown< - TPayload, - (state: TState) => TState, - (state: TState, payload: TPayload) => TState ->; +type ShouldBeTrue = IfUnknown; +//=> true -class Store { - constructor(private state: TState) {} - - execute(action: Action, payload?: TPayload): TState { - this.state = action(this.state, payload); - return this.state; - } - - // ... other methods -} - -const store = new Store({value: 1}); -declare const someExternalData: unknown; - -store.execute(state => ({value: state.value + 1})); -//=> `TPayload` is `void` - -store.execute((state, payload) => ({value: state.value + payload}), 5); -//=> `TPayload` is `5` - -store.execute((state, payload) => ({value: state.value + payload}), someExternalData); -//=> Errors: `action` is `(state: TState) => TState` +type ShouldBeBar = IfUnknown<'not unknown', 'foo', 'bar'>; +//=> 'bar' ``` @category Type Guard