mirror of
https://github.com/sindresorhus/type-fest.git
synced 2025-12-08 19:25:05 +00:00
Add ExtendsStrict type (#1165)
Co-authored-by: Som Shekhar Mukherjee <iamssmkhrj@gmail.com>
This commit is contained in:
parent
b55e2f0459
commit
d71242aee4
1
index.d.ts
vendored
1
index.d.ts
vendored
@ -186,5 +186,6 @@ export type {PackageJson} from './source/package-json.d.ts';
|
||||
export type {TsConfigJson} from './source/tsconfig-json.d.ts';
|
||||
|
||||
// Improved built-in
|
||||
export type {ExtendsStrict} from './source/extends-strict.d.ts';
|
||||
export type {ExtractStrict} from './source/extract-strict.d.ts';
|
||||
export type {ExcludeStrict} from './source/exclude-strict.d.ts';
|
||||
|
||||
@ -296,6 +296,7 @@ Click the type names for complete docs.
|
||||
|
||||
### Improved built-in
|
||||
|
||||
- [`ExtendsStrict`](source/extends-strict.d.ts) - A stricter, non-distributive version of `extends` for checking whether one type is assignable to another.
|
||||
- [`ExtractStrict`](source/extract-strict.d.ts) - A stricter version of `Extract<T, U>` that ensures every member of `U` can successfully extract something from `T`.
|
||||
- [`ExcludeStrict`](source/exclude-strict.d.ts) - A stricter version of `Exclude<T, U>` that ensures every member of `U` can successfully exclude something from `T`.
|
||||
|
||||
|
||||
42
source/extends-strict.d.ts
vendored
Normal file
42
source/extends-strict.d.ts
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
import type {IsNever} from './is-never.d.ts';
|
||||
import type {IsAny} from './is-any.d.ts';
|
||||
|
||||
/**
|
||||
A stricter, non-distributive version of `extends` for checking whether one type is assignable to another.
|
||||
|
||||
Unlike the built-in `extends` keyword, `ExtendsStrict`:
|
||||
|
||||
1. Prevents distribution over union types by wrapping both types in tuples. For example, `ExtendsStrict<string | number, number>` returns `false`, whereas `string | number extends number` would result in `boolean`.
|
||||
|
||||
2. Treats `never` as a special case: `never` doesn't extend every other type, it only extends itself (or `any`). For example, `ExtendsStrict<never, number>` returns `false` whereas `never extends number` would result in `true`.
|
||||
|
||||
@example
|
||||
```
|
||||
import type {ExtendsStrict} from 'type-fest';
|
||||
|
||||
type T1 = ExtendsStrict<number | string, string>;
|
||||
//=> false
|
||||
|
||||
type T2 = ExtendsStrict<never, number>;
|
||||
//=> false
|
||||
|
||||
type T3 = ExtendsStrict<never, never>;
|
||||
//=> true
|
||||
|
||||
type T4 = ExtendsStrict<string, number | string>;
|
||||
//=> true
|
||||
|
||||
type T5 = ExtendsStrict<string, string>;
|
||||
//=> true
|
||||
```
|
||||
|
||||
@category Improved Built-in
|
||||
*/
|
||||
export type ExtendsStrict<Left, Right> =
|
||||
IsAny<Left | Right> extends true
|
||||
? true
|
||||
: IsNever<Left> extends true
|
||||
? IsNever<Right>
|
||||
: [Left] extends [Right]
|
||||
? true
|
||||
: false;
|
||||
78
test-d/extends-strict.ts
Normal file
78
test-d/extends-strict.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import {expectType} from 'tsd';
|
||||
import type {Tagged} from '../source/tagged.d.ts';
|
||||
import type {ExtendsStrict} from '../source/extends-strict.d.ts';
|
||||
|
||||
// Basic
|
||||
expectType<ExtendsStrict<string, string>>(true);
|
||||
expectType<ExtendsStrict<number, number>>(true);
|
||||
expectType<ExtendsStrict<1, number>>(true);
|
||||
expectType<ExtendsStrict<number, 1>>(false);
|
||||
expectType<ExtendsStrict<'foo', 'foo' | 'bar'>>(true);
|
||||
expectType<ExtendsStrict<'foo' | 'bar', 'foo'>>(false);
|
||||
|
||||
// Union behavior
|
||||
expectType<ExtendsStrict<string | number, string>>(false);
|
||||
expectType<ExtendsStrict<string, string | number>>(true);
|
||||
expectType<ExtendsStrict<string | string[], string>>(false);
|
||||
expectType<ExtendsStrict<string, string | string[]>>(true);
|
||||
|
||||
// Never handling
|
||||
expectType<ExtendsStrict<never, never>>(true);
|
||||
expectType<ExtendsStrict<never, string>>(false);
|
||||
expectType<ExtendsStrict<string, never>>(false);
|
||||
|
||||
// Any and unknown
|
||||
expectType<ExtendsStrict<any, any>>(true);
|
||||
expectType<ExtendsStrict<any, never>>(true);
|
||||
expectType<ExtendsStrict<never, any>>(true);
|
||||
expectType<ExtendsStrict<any, number>>(true);
|
||||
expectType<ExtendsStrict<any, unknown>>(true); // `any` is assignable to `unknown`
|
||||
expectType<ExtendsStrict<unknown, any>>(true); // `unknown` is assignable to `any`
|
||||
expectType<ExtendsStrict<unknown, unknown>>(true);
|
||||
expectType<ExtendsStrict<string, unknown>>(true);
|
||||
expectType<ExtendsStrict<unknown, string>>(false);
|
||||
|
||||
// Tuples
|
||||
expectType<ExtendsStrict<[1, 2], number[]>>(true);
|
||||
expectType<ExtendsStrict<number[], [1, 2]>>(false);
|
||||
expectType<ExtendsStrict<[], []>>(true);
|
||||
|
||||
// Objects
|
||||
expectType<ExtendsStrict<{a: 1}, {a: number}>>(true);
|
||||
expectType<ExtendsStrict<{a: number}, {a: 1}>>(false);
|
||||
expectType<ExtendsStrict<{a: number}, {a: number; b: string}>>(false);
|
||||
expectType<ExtendsStrict<{a: number; b: string}, {a: number}>>(true);
|
||||
|
||||
// Functions
|
||||
expectType<ExtendsStrict<() => void, Function>>(true);
|
||||
expectType<ExtendsStrict<Function, () => void>>(false);
|
||||
expectType<ExtendsStrict<() => void, () => void>>(true);
|
||||
expectType<ExtendsStrict<(...args: any[]) => unknown, Function>>(true);
|
||||
|
||||
// Intersections
|
||||
expectType<ExtendsStrict<string & {bar: string}, string>>(true);
|
||||
expectType<ExtendsStrict<string, string & {bar: string}>>(false);
|
||||
|
||||
// Literal vs primitive
|
||||
expectType<ExtendsStrict<'foo', string>>(true);
|
||||
expectType<ExtendsStrict<string, 'foo'>>(false);
|
||||
|
||||
// Arrays
|
||||
expectType<ExtendsStrict<string[], string[]>>(true);
|
||||
expectType<ExtendsStrict<[string], string[]>>(true); // Tuple is assignable to array
|
||||
expectType<ExtendsStrict<string[], [string]>>(false); // Array not assignable to fixed tuple
|
||||
|
||||
// Branded types
|
||||
type UserId = Tagged<string, 'UserId'>;
|
||||
|
||||
expectType<ExtendsStrict<UserId, string>>(true);
|
||||
expectType<ExtendsStrict<string, UserId>>(false);
|
||||
expectType<ExtendsStrict<UserId, UserId>>(true);
|
||||
|
||||
// Edge meta-types
|
||||
expectType<ExtendsStrict<null, any>>(true);
|
||||
expectType<ExtendsStrict<undefined, any>>(true);
|
||||
expectType<ExtendsStrict<null, undefined>>(false);
|
||||
expectType<ExtendsStrict<undefined, null>>(false);
|
||||
expectType<ExtendsStrict<undefined, unknown>>(true);
|
||||
expectType<ExtendsStrict<null, unknown>>(true);
|
||||
Loading…
x
Reference in New Issue
Block a user