Skip to content

Commit 8d1d2d7

Browse files
authored
fix(testing): fix IsExact edge cases (denoland#5676)
1 parent 010784c commit 8d1d2d7

File tree

2 files changed

+139
-4
lines changed

2 files changed

+139
-4
lines changed

testing/types.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,13 @@ export type IsNullable<T> = Extract<T, null | undefined> extends never ? false
177177
* @typeParam T The type to check if it exactly matches type `U`.
178178
* @typeParam U The type to check if it exactly matches type `T`.
179179
*/
180-
export type IsExact<T, U> = TupleMatches<AnyToBrand<T>, AnyToBrand<U>> extends
181-
true
182-
? TupleMatches<DeepPrepareIsExact<T>, DeepPrepareIsExact<U>> extends true
183-
? true
180+
export type IsExact<T, U> = ParametersAndReturnTypeMatches<
181+
FlatType<AnyToBrand<T>>,
182+
FlatType<AnyToBrand<U>>
183+
> extends true ? ParametersAndReturnTypeMatches<
184+
FlatType<DeepPrepareIsExact<T>>,
185+
FlatType<DeepPrepareIsExact<U>>
186+
> extends true ? true
184187
: false
185188
: false;
186189

@@ -247,6 +250,16 @@ export type IsUnknown<T> = unknown extends T
247250
? ([T] extends [null] ? false : true)
248251
: false;
249252

253+
/**
254+
* The internal utility type to match the given types as return types.
255+
*
256+
* @internal
257+
*/
258+
export type ParametersAndReturnTypeMatches<T, U> = Matches<
259+
<X>(_: T) => X extends T ? 1 : 2,
260+
<X>(_: U) => X extends U ? 1 : 2
261+
>;
262+
250263
/**
251264
* The internal utility type to match the given types as tuples.
252265
*
@@ -274,3 +287,12 @@ export type AnyToBrand<T> = IsAny<T> extends true ? AnyBrand : T;
274287
* @internal
275288
*/
276289
export type AnyBrand = { __conditionalTypeChecksAny__: undefined };
290+
291+
/**
292+
* The utility type to flatten record types.
293+
*
294+
* @internal
295+
*/
296+
export type FlatType<T> = T extends Record<PropertyKey, unknown>
297+
? { [K in keyof T]: FlatType<T[K]> }
298+
: T;

testing/types_test.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ import {
3232

3333
// IsExact
3434
{
35+
class _Class<T> {
36+
declare private _prop: T;
37+
}
38+
3539
// matching
3640
assertType<IsExact<string | number, string | number>>(true);
3741
assertType<IsExact<string | number | Date, string | number | Date>>(true);
@@ -47,7 +51,56 @@ import {
4751
assertType<IsExact<{ prop: never }, { prop: never }>>(true);
4852
assertType<IsExact<{ prop: any }, { prop: any }>>(true);
4953
assertType<IsExact<{ prop: unknown }, { prop: unknown }>>(true);
54+
assertType<IsExact<{ readonly prop: any }, { readonly prop: any }>>(true);
55+
assertType<IsExact<[], []>>(true);
56+
assertType<IsExact<readonly [], readonly []>>(true);
57+
assertType<IsExact<any[], any[]>>(true);
58+
assertType<IsExact<unknown[], unknown[]>>(true);
59+
assertType<IsExact<never[], never[]>>(true);
60+
assertType<IsExact<[elm?: any], [elm?: any]>>(true);
61+
assertType<IsExact<[...any[]], [...any[]]>>(true);
62+
assertType<IsExact<[...any[], any], [...any[], any]>>(true);
63+
assertType<IsExact<[any, ...any[]], [any, ...any[]]>>(true);
64+
assertType<IsExact<[any, ...any[], any], [any, ...any[], any]>>(true);
5065
assertType<IsExact<typeof globalThis, typeof globalThis>>(true);
66+
assertType<IsExact<() => void, () => void>>(true);
67+
assertType<IsExact<() => any, () => any>>(true);
68+
assertType<IsExact<() => unknown, () => unknown>>(true);
69+
assertType<IsExact<() => never, () => never>>(true);
70+
assertType<IsExact<(arg: any) => void, (arg: any) => void>>(true);
71+
assertType<IsExact<(arg?: any) => void, (arg?: any) => void>>(true);
72+
assertType<IsExact<(...args: any[]) => void, (...args: any[]) => void>>(true);
73+
assertType<
74+
IsExact<
75+
(arg: any, ...args: any[]) => void,
76+
(arg: any, ...args: any[]) => void
77+
>
78+
>(true);
79+
assertType<IsExact<_Class<any>, _Class<any>>>(true);
80+
assertType<
81+
IsExact<
82+
_Class<{ x: any; prop?: any }>,
83+
_Class<{ x: any; prop?: any }>
84+
>
85+
>(true);
86+
assertType<
87+
IsExact<
88+
_Class<{ x: any; readonly prop: any }>,
89+
_Class<{ x: any; readonly prop: any }>
90+
>
91+
>(true);
92+
assertType<
93+
IsExact<
94+
{ [x: string]: unknown } & { prop: unknown },
95+
{ [x: string]: unknown; prop: unknown }
96+
>
97+
>(true);
98+
assertType<
99+
IsExact<
100+
{ [x: string]: unknown; prop: unknown },
101+
{ [x: string]: unknown } & { prop: unknown }
102+
>
103+
>(true);
51104

52105
// not matching
53106
assertType<IsExact<string | number | Date, string | number>>(false);
@@ -92,6 +145,66 @@ import {
92145
>
93146
>(false);
94147
assertType<IsExact<{ prop: string | undefined }, { prop?: string }>>(false); // these are different
148+
assertType<IsExact<{ prop: any }, { readonly prop: any }>>(false);
149+
assertType<IsExact<[], readonly []>>(false);
150+
assertType<IsExact<any[], []>>(false);
151+
assertType<IsExact<any[], unknown[]>>(false);
152+
assertType<IsExact<any[], never[]>>(false);
153+
assertType<IsExact<[], [elm?: any]>>(false);
154+
assertType<IsExact<[elm: any], [elm?: any]>>(false);
155+
assertType<IsExact<[...any[]], [...any[], any]>>(false);
156+
assertType<IsExact<[...any[]], [any, ...any[]]>>(false);
157+
assertType<IsExact<[...any[]], [any, ...any[], any]>>(false);
158+
assertType<IsExact<[any, ...any[]], [any, ...any[], any]>>(false);
159+
assertType<IsExact<() => void, () => undefined>>(false);
160+
assertType<IsExact<() => any, () => unknown>>(false);
161+
assertType<IsExact<() => any, () => never>>(false);
162+
assertType<IsExact<(arg: any) => void, (arg: unknown) => void>>(false);
163+
assertType<IsExact<() => void, (arg?: any) => void>>(false);
164+
assertType<IsExact<(arg: any) => void, (arg?: any) => void>>(false);
165+
assertType<
166+
IsExact<
167+
(...args: any[]) => void,
168+
(...args: unknown[]) => void
169+
>
170+
>(false);
171+
assertType<
172+
IsExact<
173+
(arg: any, ...args: any[]) => void,
174+
(arg: unknown, ...args: any[]) => void
175+
>
176+
>(false);
177+
assertType<
178+
IsExact<
179+
(arg: any, ...args: any[]) => void,
180+
(arg: any, ...args: unknown[]) => void
181+
>
182+
>(false);
183+
assertType<IsExact<_Class<any>, _Class<number>>>(false);
184+
assertType<
185+
IsExact<
186+
_Class<{ x: any; prop?: any }>,
187+
_Class<{ x: any; other?: any }>
188+
>
189+
>(false);
190+
assertType<
191+
IsExact<
192+
_Class<{ x: any; prop: any }>,
193+
_Class<{ x: any; readonly prop: any }>
194+
>
195+
>(false);
196+
assertType<
197+
IsExact<
198+
{ [x: string]: unknown; prop: any } & { prop: unknown },
199+
{ [x: string]: unknown; prop: unknown }
200+
>
201+
>(false);
202+
assertType<
203+
IsExact<
204+
{ [x: string]: unknown; prop: unknown },
205+
{ [x: string]: unknown; prop: any } & { prop: unknown }
206+
>
207+
>(false);
95208
}
96209

97210
// Has

0 commit comments

Comments
 (0)