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 IsX/IfX types for any/never/unknown #564

Merged
merged 30 commits into from Apr 5, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
344a1b2
feat: add `IsX`/`IfX` types for `any`/`never`/ `unknown`
tommy-mitchell Mar 6, 2023
e146dfd
fix: `IfAny` test case
tommy-mitchell Mar 6, 2023
3a5878e
Update is-if-any.ts
sindresorhus Mar 6, 2023
be4f89f
Update is-if-never.ts
sindresorhus Mar 6, 2023
0a0fa77
Update is-if-unknown.ts
sindresorhus Mar 6, 2023
254b7a1
chore: update `tsd`, uncomment test cases with `ts2707` error
tommy-mitchell Mar 6, 2023
546af2a
chore: separate into `is-x` and `if-x` files
tommy-mitchell Mar 6, 2023
1431554
fix: no hard wrapping
tommy-mitchell Mar 6, 2023
1036255
feat(`any`): add documentation
tommy-mitchell Mar 7, 2023
6a39a29
feat(`never`): add documentation
tommy-mitchell Mar 8, 2023
009f06d
fix(`any`): unify documentation
tommy-mitchell Mar 8, 2023
e192e47
feat: move `IsNull` to `internal`
tommy-mitchell Mar 8, 2023
11fb302
fix(`never`): add source of example
tommy-mitchell Mar 8, 2023
43f34f6
chore: add tests for `void`
tommy-mitchell Mar 8, 2023
1530ef9
feat(`unknown`): add documentation
tommy-mitchell Mar 8, 2023
b391d44
Update package.json
sindresorhus Mar 10, 2023
d0ec7b3
chore: merge changes from #563
tommy-mitchell Mar 10, 2023
d4e10d1
chore(`is-literal`): update `IsNever` import
tommy-mitchell Mar 10, 2023
eee302c
chore: set primary category as `Type Guard`
tommy-mitchell Mar 14, 2023
853cfbf
docs: reorganize readme and add note for `IsT`/`IfT` types
tommy-mitchell Mar 14, 2023
4e692d8
chore: merge upstream changes
tommy-mitchell Mar 14, 2023
dc5ba9c
docs(`IfAny`): update description and example
tommy-mitchell Mar 16, 2023
28bbfae
chore(`IfAny`): consistent variable name
tommy-mitchell Mar 16, 2023
81d38ae
chore: consistent test ordering
tommy-mitchell Mar 16, 2023
42e4b26
Merge branch 'main' into feat-expose-internals
sindresorhus Mar 17, 2023
d55804c
chore: merge changes from main
tommy-mitchell Mar 27, 2023
af4196a
docs: rename `alternate` to `conditional`
tommy-mitchell Mar 27, 2023
f61b07d
chore(`IfT`): improve descriptions and links
tommy-mitchell Mar 27, 2023
64e5639
docs(`IfT`): simplify examples
tommy-mitchell Mar 27, 2023
a15b774
Merge branch 'main' into feat-expose-internals
tommy-mitchell Mar 30, 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
3 changes: 3 additions & 0 deletions index.d.ts
Expand Up @@ -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';
Expand Down
6 changes: 6 additions & 0 deletions readme.md
Expand Up @@ -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

Expand Down
18 changes: 1 addition & 17 deletions 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 `<T>`.
Expand Down Expand Up @@ -158,23 +159,6 @@ export type IsNumeric<T extends string> = 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<T> = 0 extends 1 & T ? true : false;

/**
Returns a boolean for whether the the type is `never`.
*/
export type IsNever<T> = [T] extends [never] ? true : false;

/**
Returns a boolean for whether the the type is `unknown`.
*/
export type IsUnknown<T> = IsNever<T> extends false ? T extends unknown ? unknown extends T ? IsAny<T> 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.

Expand Down
53 changes: 53 additions & 0 deletions source/is-if-any.d.ts
@@ -0,0 +1,53 @@
/**
tommy-mitchell marked this conversation as resolved.
Show resolved Hide resolved
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<T> = (
IsAny<T> extends false
? IsNever<T> extends false
? IsUnknown<T> extends false
? IsUndefined<T> extends false
? IsNull<T> extends false
? T
: never
: never
: never
: never
: never
);
```

@category Utilities
*/
export type IsAny<T> = 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.
tommy-mitchell marked this conversation as resolved.
Show resolved Hide resolved

@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<O extends IfAny<O, never, Record<string, number>>, K extends keyof O = keyof O>(obj: O, key: K) {
return obj[key];
}
```

@category Utilities
*/
export type IfAny<T, TypeIfAny = true, TypeIfNotAny = false> = (
IsAny<T> extends true ? TypeIfAny : TypeIfNotAny
);
35 changes: 35 additions & 0 deletions 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> = [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<T, TypeIfNever = true, TypeIfNotNever = false> = (
IsNever<T> extends true ? TypeIfNever : TypeIfNotNever
);
40 changes: 40 additions & 0 deletions source/is-if-unknown.d.ts
@@ -0,0 +1,40 @@
/** Returns a boolean for whether the given type is `null`. */
type IsNull<T> = [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<T> = (
unknown extends T // `T` can be `unknown` or `any`
? IsNull<T> 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<T, TypeIfUnknown = true, TypeIfNotUnknown = false> = (
IsUnknown<T> extends true ? TypeIfUnknown : TypeIfNotUnknown
);
3 changes: 2 additions & 1 deletion 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;
Expand Down
2 changes: 1 addition & 1 deletion 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.
Expand Down
27 changes: 27 additions & 0 deletions 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<IsAny<any>>(true);
expectType<IsAny<typeof anything>>(true);
expectType<IsAny<string>>(false);
expectType<IsAny<typeof something>>(false);
expectType<IsAny<never>>(false);
expectType<IsAny<unknown>>(false);
expectType<IsAny<null>>(false);
expectType<IsAny<undefined>>(false);

// `IfAny` should return `true`/`false` if only `T` is specified
expectType<IfAny<any>>(true);
expectType<IfAny<any, 'T', 'F'>>('T');
expectType<IfAny<string>>(false);
expectType<IfAny<string, 'T', 'F'>>('F');

// Missing generic parameter
expectError<IsAny>(anything);
// TODO: commented temporarily due to SamVerschueren/tsd#173
// expectError<IfAny>(anything);

27 changes: 27 additions & 0 deletions 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<IsNever<never>>(true);
expectType<IsNever<typeof _never>>(true);
expectType<IsNever<string>>(false);
expectType<IsNever<typeof something>>(false);
expectType<IsNever<any>>(false);
expectType<IsNever<unknown>>(false);
expectType<IsNever<null>>(false);
expectType<IsNever<undefined>>(false);

// `IfNever` should return `true`/`false` if only `T` is specified
expectType<IfNever<never>>(true);
expectType<IfNever<never, 'T', 'F'>>('T');
expectType<IfNever<string>>(false);
expectType<IfNever<string, 'T', 'F'>>('F');

// Missing generic parameter
expectError<IsNever>(_never);
// TODO: commented temporarily due to SamVerschueren/tsd#173
// expectError<IfNever>(_never);

27 changes: 27 additions & 0 deletions 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<IsUnknown<unknown>>(true);
expectType<IsUnknown<typeof _unknown>>(true);
expectType<IsUnknown<string>>(false);
expectType<IsUnknown<typeof something>>(false);
expectType<IsUnknown<any>>(false);
expectType<IsUnknown<never>>(false);
expectType<IsUnknown<null>>(false);
expectType<IsUnknown<undefined>>(false);

// `IfUnknown` should return `true`/`false` if only `T` is specified
expectType<IfUnknown<unknown>>(true);
expectType<IfUnknown<unknown, 'T', 'F'>>('T');
expectType<IfUnknown<string>>(false);
expectType<IfUnknown<string, 'T', 'F'>>('F');

// Missing generic parameter
expectError<IsUnknown>(_unknown);
// TODO: commented temporarily due to SamVerschueren/tsd#173
// expectError<IfUnknown>(_unknown);