|
1 | 1 | import {expectType} from 'tsd'; |
2 | | -import {expectTypeOf} from 'expect-type'; |
3 | | -import type {RequiredDeep} from '../index.d.ts'; |
| 2 | +import type {RequiredDeep, Simplify} from '../index.d.ts'; |
| 3 | +import type {BuiltIns} from '../source/internal/type.d.ts'; |
4 | 4 |
|
5 | | -type Foo = { |
6 | | - baz?: string | undefined; |
7 | | - bar?: { |
8 | | - function?: ((...arguments_: any[]) => void) | undefined; |
9 | | - functionFixedArity?: ((argument1: unknown, argument2: unknown) => void); |
10 | | - functionWithOverload?: { |
11 | | - (argument: number): string; |
12 | | - (argument1: string, argument2: number): number; |
13 | | - }; |
14 | | - namespace?: { |
15 | | - (argument: number): string; |
16 | | - key: string | undefined; |
17 | | - }; |
18 | | - namespaceWithOverload: { |
19 | | - (argument: number): string; |
20 | | - (argument1: string, argument2: number): number; |
21 | | - key: string | undefined; |
22 | | - }; |
23 | | - object?: {key?: 'value'} | undefined; |
24 | | - string?: string | undefined; |
25 | | - number?: number | undefined; |
26 | | - boolean?: false | undefined; |
27 | | - date?: Date | undefined; |
28 | | - regexp?: RegExp | undefined; |
29 | | - symbol?: Symbol | undefined; |
30 | | - null?: null | undefined; |
31 | | - undefined?: undefined; |
32 | | - map?: Map<string | undefined, string | undefined>; |
33 | | - set?: Set<string | undefined>; |
34 | | - array?: Array<string | undefined>; |
35 | | - tuple?: ['foo' | undefined] | undefined; |
36 | | - readonlyMap?: ReadonlyMap<string | undefined, string | undefined>; |
37 | | - readonlySet?: ReadonlySet<string | undefined>; |
38 | | - readonlyArray?: ReadonlyArray<string | undefined>; |
39 | | - readonlyTuple?: readonly ['foo' | undefined] | undefined; |
40 | | - weakMap?: WeakMap<{key: string | undefined}, string | undefined>; |
41 | | - weakSet?: WeakSet<{key: string | undefined}>; |
42 | | - promise?: Promise<string | undefined>; |
43 | | - }; |
44 | | -}; |
| 5 | +expectType<RequiredDeep<{a?: number; b: string}>>({} as {a: number; b: string}); |
| 6 | +expectType<RequiredDeep<{a?: {b?: {c?: string}; d?: string}}>>({} as {a: {b: {c: string}; d: string}}); |
| 7 | +expectType<RequiredDeep<{readonly a?: number; readonly b: {c?: string}}>>({} as {readonly a: number; readonly b: {c: string}}); |
| 8 | +expectType<RequiredDeep<{a?: Array<{b?: number}>}>>({} as {a: Array<{b: number}>}); |
| 9 | +expectType<RequiredDeep<{a?: [{b?: string}, number]}>>({} as {a: [{b: string}, number]}); |
| 10 | +expectType<RequiredDeep<{a?: number | {readonly b?: string}}>>({} as {a: number | {readonly b: string}}); |
| 11 | +expectType<RequiredDeep<{a?: {b: number} | {c?: string}}>>({} as {a: {b: number} | {c: string}}); |
| 12 | +expectType<RequiredDeep<{a?: {readonly b?: BuiltIns}}>>({} as {a: {readonly b: BuiltIns}}); |
45 | 13 |
|
46 | | -type FooRequired = { |
47 | | - baz: string; |
48 | | - bar: { |
49 | | - function: (...arguments_: any[]) => void; |
50 | | - functionFixedArity: (argument1: unknown, argument2: unknown) => void; |
51 | | - functionWithOverload: { |
52 | | - (argument: number): string; |
53 | | - (argument1: string, argument2: number): number; |
54 | | - }; |
55 | | - namespace: { |
56 | | - (argument: number): string; |
57 | | - key: string; |
58 | | - }; |
59 | | - namespaceWithOverload: { |
60 | | - (argument: number): string; |
61 | | - (argument1: string, argument2: number): number; |
62 | | - key: string; |
63 | | - }; |
64 | | - object: {key: 'value'}; |
65 | | - string: string; |
66 | | - number: number; |
67 | | - boolean: false; |
68 | | - date: Date; |
69 | | - regexp: RegExp; |
70 | | - symbol: Symbol; |
71 | | - null: null; |
72 | | - undefined: never; |
73 | | - map: Map<string, string>; |
74 | | - set: Set<string>; |
75 | | - array: string[]; |
76 | | - tuple: ['foo']; |
77 | | - readonlyMap: ReadonlyMap<string, string>; |
78 | | - readonlySet: ReadonlySet<string>; |
79 | | - readonlyArray: readonly string[]; |
80 | | - readonlyTuple: readonly ['foo']; |
81 | | - weakMap: WeakMap<{key: string}, string>; |
82 | | - weakSet: WeakSet<{key: string}>; |
83 | | - promise: Promise<string>; |
84 | | - }; |
85 | | -}; |
| 14 | +// Unions |
| 15 | +expectType<RequiredDeep<{a?: number} | {b?: string}>>({} as {a: number} | {b: string}); |
| 16 | +expectType<RequiredDeep<{v?: {a?: number}} | {w?: {b?: string}}>>({} as {v: {a: number}} | {w: {b: string}}); |
| 17 | +expectType<RequiredDeep<Map<{a: {b?: {c: {d?: number}}}}, {e?: string}> | Set<{a: {b?: {c: {d?: number}}}}>>>( |
| 18 | + {} as Map<{a: {b: {c: {d: number}}}}, {e: string}> | Set<{a: {b: {c: {d: number}}}}>, |
| 19 | +); |
86 | 20 |
|
87 | | -type FooBar = Exclude<Foo['bar'], undefined>; |
88 | | -type FooRequiredBar = FooRequired['bar']; |
| 21 | +// Index signatures |
| 22 | +expectType<RequiredDeep<{[x: string]: {a?: string; b: {c?: number}}}>>({} as {[x: string]: {a: string; b: {c: number}}}); |
| 23 | +expectType<RequiredDeep<{[x: string]: {[x: number]: {c?: number}}}>>({} as {[x: string]: {[x: number]: {c: number}}}); |
89 | 24 |
|
90 | | -// TODO: Fix this case: https://github.com/mmkal/expect-type/issues/34 |
91 | | -// @ts-expect-error |
92 | | -expectTypeOf<RequiredDeep<Foo>>().toEqualTypeOf<FooRequired>(); |
93 | | -expectTypeOf<RequiredDeep<FooBar['function']>>().toEqualTypeOf<FooRequiredBar['function']>(); |
94 | | -expectTypeOf<RequiredDeep<FooBar['functionFixedArity']>>().toEqualTypeOf<FooRequiredBar['functionFixedArity']>(); |
95 | | -expectTypeOf<RequiredDeep<FooBar['object']>>().toEqualTypeOf<FooRequiredBar['object']>(); |
96 | | -expectTypeOf<RequiredDeep<FooBar['string']>>().toEqualTypeOf<FooRequiredBar['string']>(); |
97 | | -expectTypeOf<RequiredDeep<FooBar['number']>>().toEqualTypeOf<FooRequiredBar['number']>(); |
98 | | -expectTypeOf<RequiredDeep<FooBar['boolean']>>().toEqualTypeOf<FooRequiredBar['boolean']>(); |
99 | | -expectTypeOf<RequiredDeep<FooBar['date']>>().toEqualTypeOf<FooRequiredBar['date']>(); |
100 | | -expectTypeOf<RequiredDeep<FooBar['regexp']>>().toEqualTypeOf<FooRequiredBar['regexp']>(); |
101 | | -expectTypeOf<RequiredDeep<FooBar['map']>>().toEqualTypeOf<FooRequiredBar['map']>(); |
102 | | -expectTypeOf<RequiredDeep<FooBar['set']>>().toEqualTypeOf<FooRequiredBar['set']>(); |
103 | | -expectTypeOf<RequiredDeep<FooBar['array']>>().toEqualTypeOf<FooRequiredBar['array']>(); |
104 | | -expectTypeOf<RequiredDeep<FooBar['tuple']>>().toEqualTypeOf<FooRequiredBar['tuple']>(); |
105 | | -expectTypeOf<RequiredDeep<FooBar['readonlyMap']>>().toEqualTypeOf<FooRequiredBar['readonlyMap']>(); |
106 | | -expectTypeOf<RequiredDeep<FooBar['readonlySet']>>().toEqualTypeOf<FooRequiredBar['readonlySet']>(); |
107 | | -expectTypeOf<RequiredDeep<FooBar['readonlyArray']>>().toEqualTypeOf<FooRequiredBar['readonlyArray']>(); |
108 | | -expectTypeOf<RequiredDeep<FooBar['readonlyTuple']>>().toEqualTypeOf<FooRequiredBar['readonlyTuple']>(); |
109 | | -expectTypeOf<RequiredDeep<FooBar['weakMap']>>().toEqualTypeOf<FooRequiredBar['weakMap']>(); |
110 | | -expectTypeOf<RequiredDeep<FooBar['weakSet']>>().toEqualTypeOf<FooRequiredBar['weakSet']>(); |
111 | | -expectTypeOf<RequiredDeep<FooBar['promise']>>().toEqualTypeOf<FooRequiredBar['promise']>(); |
| 25 | +// Optional with `undefined` |
| 26 | +// This behaviour changes depending on `exactOptionalPropertyTypes` compiler option, refer https://github.com/sindresorhus/type-fest/issues/1217 |
| 27 | +expectType<RequiredDeep<{a?: number | undefined}>>({} as {a: number | undefined}); |
| 28 | +expectType<RequiredDeep<{a?: {b: number} | undefined}>>({} as {a: {b: number} | undefined}); |
| 29 | +expectType<RequiredDeep<{a?: undefined}>>({} as {a: undefined}); |
112 | 30 |
|
113 | | -// TODO: Fix this case: https://github.com/mmkal/expect-type/issues/34 |
114 | | -// @ts-expect-error |
115 | | -expectTypeOf<RequiredDeep<FooBar['namespace']>>().toEqualTypeOf<FooRequiredBar['namespace']>(); |
| 31 | +// Tuples |
| 32 | +expectType<RequiredDeep<[string, number, boolean]>>({} as [string, number, boolean]); // All required |
| 33 | +expectType<RequiredDeep<readonly [string, number?, boolean?]>>({} as readonly [string, number, boolean]); // Required and optional |
| 34 | +expectType<RequiredDeep<[string?, number?, boolean?]>>({} as [string, number, boolean]); // All optional |
| 35 | +expectType<RequiredDeep<[string, number, ...boolean[]]>>({} as [string, number, ...boolean[]]); // Required and trailing rest |
| 36 | +expectType<RequiredDeep<readonly [string, number?, ...boolean[]]>>({} as readonly [string, number, ...boolean[]]); // Required, optional and trailing rest |
| 37 | +expectType<RequiredDeep<[string?, number?, ...boolean[]]>>({} as [string, number, ...boolean[]]); // Optional and trailing rest |
| 38 | +expectType<RequiredDeep<[...string[], number, boolean]>>({} as [...string[], number, boolean]); // Leading rest |
| 39 | +expectType<RequiredDeep<[number, ...string[], number, boolean]>>({} as [number, ...string[], number, boolean]); // Rest in middle |
| 40 | + |
| 41 | +// Nested tuples |
| 42 | +expectType<RequiredDeep<[[string, number?], [boolean, Date]?, [RegExp?, number?]?]>>({} as [[string, number], [boolean, Date], [RegExp, number]]); |
| 43 | +expectType<RequiredDeep<[[{a?: string}, {b: number; c?: string}?], ...Array<[boolean, boolean?]>]>>({} as [[{a: string}, {b: number; c: string}], ...Array<[boolean, boolean]>]); |
| 44 | + |
| 45 | +// Non-tuple arrays |
| 46 | +expectType<RequiredDeep<string[]>>({} as string[]); |
| 47 | +expectType<RequiredDeep<Array<string | undefined>>>({} as string[]); |
| 48 | +expectType<RequiredDeep<ReadonlyArray<string | undefined>>>({} as readonly string[]); |
| 49 | + |
| 50 | +// Maps |
| 51 | +expectType<RequiredDeep<Map<{a?: string; b?: number}, {a?: string; b?: number}>>>({} as Map<{a: string; b: number}, {a: string; b: number}>); |
| 52 | +expectType<RequiredDeep<ReadonlyMap<{a?: string; b?: number}, {a?: string; b?: number}>>>({} as ReadonlyMap<{a: string; b: number}, {a: string; b: number}>); |
| 53 | +expectType<RequiredDeep<WeakMap<{a?: string; b?: number}, {a?: string; b?: number}>>>({} as WeakMap<{a: string; b: number}, {a: string; b: number}>); |
116 | 54 |
|
117 | | -expectTypeOf<RequiredDeep<FooBar['undefined']>>().toBeNever(); |
118 | | -expectTypeOf<RequiredDeep<FooBar['null']>>().toEqualTypeOf<FooRequiredBar['null']>(); |
| 55 | +// Sets |
| 56 | +expectType<RequiredDeep<Set<{a?: string; b?: number}>>>({} as Set<{a: string; b: number}>); |
| 57 | +expectType<RequiredDeep<ReadonlySet<{a?: string; b?: number}>>>({} as ReadonlySet<{a: string; b: number}>); |
| 58 | +expectType<RequiredDeep<WeakSet<{a?: string; b?: number}>>>({} as WeakSet<{a: string; b: number}>); |
119 | 59 |
|
120 | | -// These currently need to be left alone due to TypeScript limitations. |
121 | | -// @see https://github.com/microsoft/TypeScript/issues/29732 |
122 | | -expectType<string>(({} as unknown as RequiredDeep<FooBar['functionWithOverload']>)(0)); |
123 | | -expectType<number>(({} as unknown as RequiredDeep<FooBar['functionWithOverload']>)('foo', 0)); |
124 | | -expectType<string>(({} as unknown as RequiredDeep<FooBar['namespaceWithOverload']>)(0)); |
125 | | -expectType<number>(({} as unknown as RequiredDeep<FooBar['namespaceWithOverload']>)('foo', 0)); |
| 60 | +// Promises |
| 61 | +expectType<RequiredDeep<Promise<{a?: string; b?: number}>>>({} as Promise<{a: string; b: number}>); |
| 62 | +expectType<RequiredDeep<Promise<Promise<{a?: string; b?: [number, number?]}>>>>({} as Promise<Promise<{a: string; b: [number, number]}>>); |
| 63 | + |
| 64 | +// Functions |
| 65 | +type FunctionWithProperties = {(a1: string, a2: number): boolean; p1?: string; readonly p2?: number}; |
| 66 | +declare const functionWithProperties: RequiredDeep<FunctionWithProperties>; |
| 67 | +expectType<boolean>(functionWithProperties('foo', 1)); |
| 68 | +expectType<{p1: string; readonly p2: number}>({} as Simplify<typeof functionWithProperties>); // `Simplify` removes the call signature from `typeof functionWithProperties` |
| 69 | + |
| 70 | +type FunctionWithProperties2 = {(a1: boolean, ...a2: string[]): number; p1?: {p2?: string; p3: {readonly p4?: boolean}}}; |
| 71 | +declare const functionWithProperties2: RequiredDeep<FunctionWithProperties2>; |
| 72 | +expectType<number>(functionWithProperties2(true, 'foo', 'bar')); |
| 73 | +expectType<{p1: {p2: string; p3: {readonly p4: boolean}}}>({} as Simplify<typeof functionWithProperties2>); |
| 74 | + |
| 75 | +type FunctionWithProperties3 = {(): void; p1?: {p2?: string; p3: [{p4?: number}, string?]}}; |
| 76 | +declare const functionWithProperties3: RequiredDeep<FunctionWithProperties3>; |
| 77 | +expectType<void>(functionWithProperties3()); |
| 78 | +expectType<{p1: {p2: string; p3: [{p4: number}, string]}}>({} as Simplify<typeof functionWithProperties3>); |
| 79 | + |
| 80 | +// Properties within functions containing multiple call signatures are not made required due to TS limitations, refer https://github.com/microsoft/TypeScript/issues/29732 |
| 81 | +type FunctionWithProperties4 = {(a1: number): string; (a1: string, a2: number): number; p1?: string}; |
| 82 | +declare const functionWithProperties4: RequiredDeep<FunctionWithProperties4>; |
| 83 | +// @ts-expect-error |
| 84 | +expectType<{p1: string}>({} as Simplify<typeof functionWithProperties4>); |
0 commit comments