Skip to content

Commit 7de51cc

Browse files
committed
feat(parser): circular references
1 parent 1302502 commit 7de51cc

File tree

1 file changed

+27
-13
lines changed

1 file changed

+27
-13
lines changed

Diff for: src/parser.ts

+27-13
Original file line numberDiff line numberDiff line change
@@ -183,23 +183,37 @@ export function parseFromProgram(filePath: string, program: ts.Program) {
183183
return;
184184
}
185185

186-
programNode.body.push(t.componentNode(name, properties.map(checkSymbol)));
186+
programNode.body.push(
187+
t.componentNode(name, properties.map(x => checkSymbol(x, [(type as any).id]))),
188+
);
187189
}
188190

189-
function checkSymbol(symbol: ts.Symbol): t.PropTypeNode {
190-
const locations = symbol.getDeclarations();
191-
if (!locations) {
192-
console.error('No types found');
193-
return t.propTypeNode(symbol.getName(), getDocumentation(symbol), t.anyNode());
191+
function checkSymbol(symbol: ts.Symbol, typeStack: number[]): t.PropTypeNode {
192+
const declarations = symbol.getDeclarations();
193+
194+
const type = declarations
195+
? // The proptypes aren't detailed enough that we need all the different combinations
196+
// so we just pick the first and ignore the rest
197+
checker.getTypeOfSymbolAtLocation(symbol, declarations[0])
198+
: // The properties of Record<..., ...> don't have a declaration, but the symbol has a type property
199+
((symbol as any).type as ts.Type);
200+
201+
if (!type) {
202+
throw new Error('No types found');
194203
}
195204

196-
// The proptypes aren't detailed enough that we need all the different combinations
197-
// So we just pick the first and ignore the rest
198-
const type = checker.getTypeOfSymbolAtLocation(symbol, locations[0]);
199-
return t.propTypeNode(symbol.getName(), getDocumentation(symbol), checkType(type));
205+
return t.propTypeNode(
206+
symbol.getName(),
207+
getDocumentation(symbol),
208+
// If the typeStack contains type.id we're dealing with a type that references itself.
209+
// To prevent getting stuck in an infinite loop we just set it to an objectNode
210+
typeStack.includes((type as any).id)
211+
? t.objectNode()
212+
: checkType(type, [...typeStack, (type as any).id]),
213+
);
200214
}
201215

202-
function checkType(type: ts.Type): t.Node {
216+
function checkType(type: ts.Type, typeStack: number[]): t.Node {
203217
{
204218
const typeNode = type as any;
205219

@@ -220,11 +234,11 @@ export function parseFromProgram(filePath: string, program: ts.Program) {
220234
if (checker.isArrayType(type)) {
221235
// @ts-ignore - Private method
222236
const arrayType: ts.Type = checker.getElementTypeOfArrayType(type);
223-
return t.arrayNode(checkType(arrayType));
237+
return t.arrayNode(checkType(arrayType, typeStack));
224238
}
225239

226240
if (type.isUnion()) {
227-
return t.unionNode(type.types.map(checkType));
241+
return t.unionNode(type.types.map(x => checkType(x, typeStack)));
228242
}
229243

230244
if (type.flags & ts.TypeFlags.String) {

0 commit comments

Comments
 (0)