/
types.ts
127 lines (112 loc) · 3.92 KB
/
types.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
// Copyright @dsherret and dsherret/conditional-type-checks contributors. All rights reserved. MIT license.
/**
* Asserts at compile time that the provided type argument's type resolves to the expected boolean literal type.
* @param expectTrue - True if the passed in type argument resolved to true.
* @example
* ```typescript, ignore
* import { assertType, IsExact, IsNullable } from "https://deno.land/std@$STD_VERSION/testing/types.ts";
*
* const result = "some result" as string | number;
*
* // compile error if the type of `result` is not exactly `string | number`
* assertType<IsExact<typeof result, string | number>>(true);
*
* // causes a compile error that `true` is not assignable to `false`
* assertType<IsNullable<string>>(true); // error: string is not nullable
* ```
*/
export function assertType<T extends true | false>(_expectTrue: T) {
}
/**
* Asserts at compile time that the provided type argument's type resolves to true.
*
* @example
* ```typescript
* import { AssertTrue, Has, IsNullable } from "https://deno.land/std@$STD_VERSION/testing/types.ts";
*
* const result = 1 as string | number | null;
*
* type doTest = AssertTrue<Has<typeof result, string> | IsNullable<typeof result>>;
* ```
*/
export type AssertTrue<T extends true> = never;
/**
* Asserts at compile time that the provided type argument's type resolves to false.
* @example
* ```typescript
* import { AssertFalse, IsNever } from "https://deno.land/std@$STD_VERSION/testing/types.ts";
*
* const result = 1 as string | number | null;
*
* type doTest = AssertFalse<IsNever<typeof result>>;
* ```
*/
export type AssertFalse<T extends false> = never;
/**
* Asserts at compile time that the provided type argument's type resolves to the expected boolean literal type.
* @example
* ```typescript
* import { Assert, Has } from "https://deno.land/std@$STD_VERSION/testing/types.ts";
*
* const result = 1 as string | number | null;
*
* type doTest = Assert<Has<typeof result, number>, true>;
* ```
*/
export type Assert<T extends true | false, Expected extends T> = never;
/**
* Checks if type `T` has the specified type `U`.
*/
export type Has<T, U> = IsAny<T> extends true ? true
: IsAny<U> extends true ? false
: Extract<T, U> extends never ? false
: true;
/**
* Checks if type `T` does not have the specified type `U`.
*/
export type NotHas<T, U> = Has<T, U> extends false ? true : false;
/**
* Checks if type `T` is possibly null or undefined.
*/
export type IsNullable<T> = Extract<T, null | undefined> extends never ? false
: true;
/**
* Checks if type `T` exactly matches type `U`.
*/
export type IsExact<T, U> = TupleMatches<AnyToBrand<T>, AnyToBrand<U>> extends
true
? TupleMatches<DeepPrepareIsExact<T>, DeepPrepareIsExact<U>> extends true
? true
: false
: false;
type DeepPrepareIsExact<T, VisitedTypes = never> = {
// make optional properties required
[P in keyof T]-?: IsAny<T[P]> extends true ? AnyBrand
: DeepPrepareIsExactProp<T[P], T, VisitedTypes>;
};
type DeepPrepareIsExactProp<Prop, Parent, VisitedTypes> = Prop extends
VisitedTypes
// recursive, bail
? Prop
// not recursive, keep going and add the parent type as a visited type
: DeepPrepareIsExact<Prop, VisitedTypes | Parent>;
/**
* Checks if type `T` is the `any` type.
*/
// https://stackoverflow.com/a/49928360/3406963
export type IsAny<T> = 0 extends (1 & T) ? true : false;
/**
* Checks if type `T` is the `never` type.
*/
export type IsNever<T> = [T] extends [never] ? true : false;
/**
* Checks if type `T` is the `unknown` type.
*/
export type IsUnknown<T> = unknown extends T
? ([T] extends [null] ? false : true)
: false;
type TupleMatches<T, U> = Matches<[T], [U]>;
type Matches<T, U> = T extends U ? U extends T ? true : false : false;
type AnyToBrand<T> = IsAny<T> extends true ? AnyBrand : T;
type AnyBrand = { __conditionalTypeChecksAny__: undefined };