Skip to content

Commit a21d7b8

Browse files
committed
Resolve recursive named unions correctly
1 parent 8158db3 commit a21d7b8

File tree

3 files changed

+83
-18
lines changed

3 files changed

+83
-18
lines changed

src/parsers/typeResolver.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,17 @@ export function resolveType(
2525
): TypeNode {
2626
const { checker, typeStack, includeExternalTypes } = context;
2727

28+
const typeId = getTypeId(type);
29+
2830
// If the typeStack contains type.id we're dealing with an object that references itself.
2931
// To prevent getting stuck in an infinite loop we just set it to an objectNode
30-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
31-
if (typeStack.includes((type as any).id)) {
32+
if (typeId !== undefined && typeStack.includes(typeId)) {
3233
return new ObjectNode(undefined, [], undefined);
3334
}
3435

35-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
36-
typeStack.push((type as any).id);
36+
if (typeId !== undefined) {
37+
typeStack.push(typeId);
38+
}
3739

3840
try {
3941
if (type.flags & ts.TypeFlags.TypeParameter && type.symbol) {
@@ -97,7 +99,9 @@ export function resolveType(
9799
// @ts-expect-error - Internal API
98100
if (type.origin?.isUnion()) {
99101
// @ts-expect-error - Internal API
100-
return resolveType(type.origin, name, context);
102+
for (const memberType of type.origin.types) {
103+
memberTypes.push(resolveType(memberType, memberType.getSymbol()?.name || '', context));
104+
}
101105
} else {
102106
for (const memberType of type.types) {
103107
memberTypes.push(resolveType(memberType, memberType.getSymbol()?.name || '', context));
@@ -243,6 +247,9 @@ export function resolveType(
243247
);
244248

245249
return new IntrinsicNode('any');
250+
} catch (error) {
251+
console.error(error);
252+
throw error;
246253
} finally {
247254
typeStack.pop();
248255
}
@@ -319,3 +326,8 @@ function getTypeName(type: ts.Type, checker: ts.TypeChecker): string {
319326
function hasFlag(typeFlags: number, flag: number) {
320327
return (typeFlags & flag) === flag;
321328
}
329+
330+
function getTypeId(type: ts.Type): number | undefined {
331+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
332+
return (type as any).id;
333+
}

test/literal-unions/input.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export function test2(
66
referencedStringUnion: StringUnion,
77
referencedNumberUnion: NumberUnion,
88
unionOfUnions: StringUnion | NumberUnion,
9+
indirectUnion: IndirectStringUnion | undefined,
910
) {}
1011

1112
export function test3(prop: keyof Params) {}
@@ -17,8 +18,9 @@ export interface Params {
1718
referencedNumberUnion: NumberUnion;
1819
callback: (ref: StringUnion | undefined) => void;
1920
unionOfUnions: StringUnion | NumberUnion;
20-
unionAndLiteral: StringUnion | 'qux';
21+
indirectUnion: IndirectStringUnion | undefined;
2122
}
2223

2324
type StringUnion = 'foo' | 'bar' | 'baz';
25+
type IndirectStringUnion = StringUnion | 'qux';
2426
type NumberUnion = 1 | 2 | 3;

test/literal-unions/output.json

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -196,31 +196,41 @@
196196
"optional": false
197197
},
198198
{
199-
"name": "unionAndLiteral",
199+
"name": "indirectUnion",
200200
"type": {
201201
"kind": "union",
202202
"types": [
203203
{
204204
"kind": "union",
205-
"name": "StringUnion",
205+
"name": "IndirectStringUnion",
206206
"types": [
207207
{
208-
"kind": "literal",
209-
"value": "\"foo\""
210-
},
211-
{
212-
"kind": "literal",
213-
"value": "\"bar\""
208+
"kind": "union",
209+
"name": "StringUnion",
210+
"types": [
211+
{
212+
"kind": "literal",
213+
"value": "\"foo\""
214+
},
215+
{
216+
"kind": "literal",
217+
"value": "\"bar\""
218+
},
219+
{
220+
"kind": "literal",
221+
"value": "\"baz\""
222+
}
223+
]
214224
},
215225
{
216226
"kind": "literal",
217-
"value": "\"baz\""
227+
"value": "\"qux\""
218228
}
219229
]
220230
},
221231
{
222-
"kind": "literal",
223-
"value": "\"qux\""
232+
"kind": "intrinsic",
233+
"name": "undefined"
224234
}
225235
]
226236
},
@@ -376,6 +386,47 @@
376386
}
377387
]
378388
}
389+
},
390+
{
391+
"nodeType": "parameter",
392+
"name": "indirectUnion",
393+
"type": {
394+
"kind": "union",
395+
"types": [
396+
{
397+
"kind": "union",
398+
"name": "IndirectStringUnion",
399+
"types": [
400+
{
401+
"kind": "union",
402+
"name": "StringUnion",
403+
"types": [
404+
{
405+
"kind": "literal",
406+
"value": "\"foo\""
407+
},
408+
{
409+
"kind": "literal",
410+
"value": "\"bar\""
411+
},
412+
{
413+
"kind": "literal",
414+
"value": "\"baz\""
415+
}
416+
]
417+
},
418+
{
419+
"kind": "literal",
420+
"value": "\"qux\""
421+
}
422+
]
423+
},
424+
{
425+
"kind": "intrinsic",
426+
"name": "undefined"
427+
}
428+
]
429+
}
379430
}
380431
],
381432
"returnValueType": {
@@ -426,7 +477,7 @@
426477
},
427478
{
428479
"kind": "literal",
429-
"value": "\"unionAndLiteral\""
480+
"value": "\"indirectUnion\""
430481
}
431482
]
432483
}

0 commit comments

Comments
 (0)