From fc4871ed6abdc980ccc4db6209c183d7ab294ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dudak?= Date: Mon, 19 May 2025 17:23:48 +0200 Subject: [PATCH 01/10] Remove unnecessary name parameter from typeResolver --- src/parsers/exportParser.ts | 2 +- src/parsers/functionParser.ts | 8 +--- src/parsers/objectParser.ts | 13 +++--- src/parsers/propertyParser.ts | 2 +- src/parsers/typeResolver.ts | 43 +++++++++---------- test/aliases/input.ts | 6 +++ test/aliases/output.json | 36 ++++++++++++++++ test/namespaces/input.tsx | 5 +++ test/namespaces/output.json | 78 +++++++++++++++++++++++++++++++++++ test/tuples/output.json | 2 + 10 files changed, 159 insertions(+), 36 deletions(-) create mode 100644 test/aliases/input.ts create mode 100644 test/aliases/output.json diff --git a/src/parsers/exportParser.ts b/src/parsers/exportParser.ts index 84ff402..c375daa 100644 --- a/src/parsers/exportParser.ts +++ b/src/parsers/exportParser.ts @@ -149,7 +149,7 @@ export function parseExport( type: ts.Type, parentNamespaces: string[], ) { - const parsedType = resolveType(type, symbol.getName(), parserContext); + const parsedType = resolveType(type, parserContext); if (parsedType) { // Patch parentNamespaces if the type supports it if (parsedType && 'parentNamespaces' in parsedType) { diff --git a/src/parsers/functionParser.ts b/src/parsers/functionParser.ts index 0aaea98..6378181 100644 --- a/src/parsers/functionParser.ts +++ b/src/parsers/functionParser.ts @@ -75,11 +75,7 @@ function parseFunctionSignature( parseParameter(parameterSymbol, context, skipResolvingComplexTypes), ); - const returnValueType = resolveType( - signature.getReturnType(), - signature.getDeclaration().name?.getText() || '', - context, - ); + const returnValueType = resolveType(signature.getReturnType(), context); return new CallSignature(parameters, returnValueType); } @@ -94,9 +90,9 @@ function parseParameter( try { const parameterDeclaration = parameterSymbol.valueDeclaration as ts.ParameterDeclaration; + const parameterType = resolveType( checker.getTypeOfSymbolAtLocation(parameterSymbol, parameterSymbol.valueDeclaration!), - parameterSymbol.getName(), context, skipResolvingComplexTypes, ); diff --git a/src/parsers/objectParser.ts b/src/parsers/objectParser.ts index 597320a..42895cf 100644 --- a/src/parsers/objectParser.ts +++ b/src/parsers/objectParser.ts @@ -6,7 +6,6 @@ import { getTypeNamespaces } from './typeResolver'; export function parseObjectType( type: ts.Type, - name: string, context: ParserContext, skipResolvingComplexTypes: boolean, ): ObjectNode | undefined { @@ -25,9 +24,13 @@ export function parseObjectType( if (properties.length) { if ( !skipResolvingComplexTypes && - shouldResolveObject({ name, propertyCount: properties.length, depth: typeStack.length }) + shouldResolveObject({ + name: typeName ?? '', + propertyCount: properties.length, + depth: typeStack.length, + }) ) { - const filtered = properties.filter((property) => { + const filteredProperties = properties.filter((property) => { const declaration = property.valueDeclaration ?? (property.declarations?.[0] as ts.PropertySignature | undefined); @@ -37,11 +40,11 @@ export function parseObjectType( shouldInclude({ name: property.getName(), depth: typeStack.length + 1 }) ); }); - if (filtered.length > 0) { + if (filteredProperties.length > 0) { return new ObjectNode( typeName, getTypeNamespaces(type), - filtered.map((property) => { + filteredProperties.map((property) => { return parseProperty( property, property.valueDeclaration as ts.PropertySignature, diff --git a/src/parsers/propertyParser.ts b/src/parsers/propertyParser.ts index 6d2b2c2..70bb535 100644 --- a/src/parsers/propertyParser.ts +++ b/src/parsers/propertyParser.ts @@ -36,7 +36,7 @@ export function parseProperty( parsedType = new IntrinsicNode('any'); isOptional = Boolean(propertySignature.questionToken); } else { - parsedType = resolveType(type, propertySymbol.getName(), context, skipResolvingComplexTypes); + parsedType = resolveType(type, context, skipResolvingComplexTypes); isOptional = Boolean(propertySymbol.flags & ts.SymbolFlags.Optional); } diff --git a/src/parsers/typeResolver.ts b/src/parsers/typeResolver.ts index 21f32c1..454be0d 100644 --- a/src/parsers/typeResolver.ts +++ b/src/parsers/typeResolver.ts @@ -19,7 +19,6 @@ import { export function resolveType( type: ts.Type, - name: string, context: ParserContext, skipResolvingComplexTypes: boolean = false, ): TypeNode { @@ -47,7 +46,7 @@ export function resolveType( namespaces, declaration?.constraint?.getText(), declaration?.default - ? resolveType(checker.getTypeAtLocation(declaration.default), '', context) + ? resolveType(checker.getTypeAtLocation(declaration.default), context) : undefined, ); } @@ -55,11 +54,7 @@ export function resolveType( if (checker.isArrayType(type)) { // @ts-expect-error - Private method const arrayType: ts.Type = checker.getElementTypeOfArrayType(type); - return new ArrayNode( - type.aliasSymbol?.name, - namespaces, - resolveType(arrayType, name, context), - ); + return new ArrayNode(type.aliasSymbol?.name, namespaces, resolveType(arrayType, context)); } if (!includeExternalTypes && isTypeExternal(type, checker)) { @@ -106,11 +101,11 @@ export function resolveType( if (type.origin?.isUnion()) { // @ts-expect-error - Internal API for (const memberType of type.origin.types) { - memberTypes.push(resolveType(memberType, memberType.getSymbol()?.name || '', context)); + memberTypes.push(resolveType(memberType, context)); } } else { for (const memberType of type.types) { - memberTypes.push(resolveType(memberType, memberType.getSymbol()?.name || '', context)); + memberTypes.push(resolveType(memberType, context)); } } @@ -128,7 +123,7 @@ export function resolveType( } for (const memberType of type.types) { - memberTypes.push(resolveType(memberType, memberType.getSymbol()?.name || '', context)); + memberTypes.push(resolveType(memberType, context)); } if (memberTypes.length === 0) { @@ -149,7 +144,7 @@ export function resolveType( return parseFunctionType(type, context)!; } - const objectType = parseObjectType(type, name, context, skipResolvingComplexTypes); + const objectType = parseObjectType(type, context, skipResolvingComplexTypes); if (objectType) { return new IntersectionNode(typeName, namespaces, memberTypes, objectType.properties); } @@ -160,11 +155,9 @@ export function resolveType( if (checker.isTupleType(type)) { return new TupleNode( - undefined, + type.aliasSymbol?.name, [], - (type as ts.TupleType).typeArguments?.map((x) => - resolveType(x, x.getSymbol()?.name || '', context), - ) ?? [], + (type as ts.TupleType).typeArguments?.map((x) => resolveType(x, context)) ?? [], ); } @@ -218,7 +211,7 @@ export function resolveType( return parseFunctionType(type, context)!; } - const objectType = parseObjectType(type, name, context, skipResolvingComplexTypes); + const objectType = parseObjectType(type, context, skipResolvingComplexTypes); if (objectType) { return objectType; } @@ -247,7 +240,7 @@ export function resolveType( const trueType = checker.getTypeFromTypeNode( type.aliasSymbol.declarations[0].type.trueType, ); - return resolveType(trueType, name, context); + return resolveType(trueType, context); } } @@ -292,19 +285,23 @@ export function getTypeNamespaces(type: ts.Type): string[] { } const declaration = symbol.valueDeclaration ?? symbol.declarations?.[0]; - if (!declaration) { + return getNodeNamespaces(declaration); +} + +export function getNodeNamespaces(node: ts.Node | undefined): string[] { + if (!node) { return []; } const namespaces: string[] = []; - let currentDeclaration: ts.Node = declaration.parent; + let currentNode = node.parent; - while (currentDeclaration != null && !ts.isSourceFile(currentDeclaration)) { - if (ts.isModuleDeclaration(currentDeclaration)) { - namespaces.unshift(currentDeclaration.name.getText()); + while (currentNode != null && !ts.isSourceFile(currentNode)) { + if (ts.isModuleDeclaration(currentNode)) { + namespaces.unshift(currentNode.name.getText()); } - currentDeclaration = currentDeclaration.parent; + currentNode = currentNode.parent; } return namespaces; diff --git a/test/aliases/input.ts b/test/aliases/input.ts new file mode 100644 index 0000000..9f7cfad --- /dev/null +++ b/test/aliases/input.ts @@ -0,0 +1,6 @@ +export interface A { + a: Alias; +} + +type SomeType = 1 | 2; +type Alias = SomeType; diff --git a/test/aliases/output.json b/test/aliases/output.json new file mode 100644 index 0000000..6805307 --- /dev/null +++ b/test/aliases/output.json @@ -0,0 +1,36 @@ +{ + "name": "test/aliases/input", + "exports": [ + { + "name": "A", + "type": { + "kind": "object", + "name": "A", + "parentNamespaces": [], + "properties": [ + { + "name": "a", + "type": { + "kind": "union", + "types": [ + { + "kind": "literal", + "parentNamespaces": [], + "value": 1 + }, + { + "kind": "literal", + "parentNamespaces": [], + "value": 2 + } + ], + "name": "SomeType", + "parentNamespaces": [] + }, + "optional": false + } + ] + } + } + ] +} \ No newline at end of file diff --git a/test/namespaces/input.tsx b/test/namespaces/input.tsx index d57bb89..1532b2b 100644 --- a/test/namespaces/input.tsx +++ b/test/namespaces/input.tsx @@ -4,6 +4,7 @@ export namespace Root { export interface Params { s: Grade; + a: NamespacedType; } export enum Grade { @@ -21,6 +22,10 @@ export namespace Root { } export function fn2() {} + + export type NamespacedType = OutsideType; } export function fn3(params: Root.Sub.Params) {} + +type OutsideType = 'one' | 'two' | 'three'; diff --git a/test/namespaces/output.json b/test/namespaces/output.json index 2323452..471181c 100644 --- a/test/namespaces/output.json +++ b/test/namespaces/output.json @@ -40,6 +40,32 @@ ] }, "optional": false + }, + { + "name": "a", + "type": { + "kind": "union", + "types": [ + { + "kind": "literal", + "parentNamespaces": [], + "value": "\"one\"" + }, + { + "kind": "literal", + "parentNamespaces": [], + "value": "\"two\"" + }, + { + "kind": "literal", + "parentNamespaces": [], + "value": "\"three\"" + } + ], + "name": "OutsideType", + "parentNamespaces": [] + }, + "optional": false } ] }, @@ -118,6 +144,32 @@ ] }, "optional": false + }, + { + "name": "a", + "type": { + "kind": "union", + "types": [ + { + "kind": "literal", + "parentNamespaces": [], + "value": "\"one\"" + }, + { + "kind": "literal", + "parentNamespaces": [], + "value": "\"two\"" + }, + { + "kind": "literal", + "parentNamespaces": [], + "value": "\"three\"" + } + ], + "name": "OutsideType", + "parentNamespaces": [] + }, + "optional": false } ] }, @@ -202,6 +254,32 @@ ] }, "optional": false + }, + { + "name": "a", + "type": { + "kind": "union", + "types": [ + { + "kind": "literal", + "parentNamespaces": [], + "value": "\"one\"" + }, + { + "kind": "literal", + "parentNamespaces": [], + "value": "\"two\"" + }, + { + "kind": "literal", + "parentNamespaces": [], + "value": "\"three\"" + } + ], + "name": "OutsideType", + "parentNamespaces": [] + }, + "optional": false } ] } diff --git a/test/tuples/output.json b/test/tuples/output.json index 41c8aa9..2877842 100644 --- a/test/tuples/output.json +++ b/test/tuples/output.json @@ -70,6 +70,7 @@ "parameters": [], "returnValueType": { "kind": "tuple", + "name": "TupleType", "parentNamespaces": [], "types": [ { @@ -99,6 +100,7 @@ "parameters": [], "returnValueType": { "kind": "tuple", + "name": "NamedTupleType", "parentNamespaces": [], "types": [ { From 1bc7b550a31d6ae5947b4477143830c2d8522eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dudak?= Date: Tue, 20 May 2025 11:26:26 +0200 Subject: [PATCH 02/10] Handle type aliases better --- src/parsers/functionParser.ts | 1 + src/parsers/propertyParser.ts | 9 +-- src/parsers/typeResolver.ts | 51 +++++++++++---- test/aliases/output.json | 2 +- test/arrays/output.json | 1 + test/base-ui-component/output.json | 7 +- test/hook-arrow-function/output.json | 4 +- test/hook-function-expression/output.json | 4 +- test/hook-function/output.json | 4 +- test/mui-overridable-component/output.json | 5 +- test/namespaces/input.tsx | 4 +- test/namespaces/output.json | 75 ++++++++++++++++------ test/react-event-handlers/output.json | 10 ++- test/react-refs/output.json | 19 ++++-- test/ts-utility-types/output.json | 1 + 15 files changed, 145 insertions(+), 52 deletions(-) diff --git a/src/parsers/functionParser.ts b/src/parsers/functionParser.ts index 6378181..29ce149 100644 --- a/src/parsers/functionParser.ts +++ b/src/parsers/functionParser.ts @@ -94,6 +94,7 @@ function parseParameter( const parameterType = resolveType( checker.getTypeOfSymbolAtLocation(parameterSymbol, parameterSymbol.valueDeclaration!), context, + parameterDeclaration.type, skipResolvingComplexTypes, ); diff --git a/src/parsers/propertyParser.ts b/src/parsers/propertyParser.ts index 70bb535..f045c7b 100644 --- a/src/parsers/propertyParser.ts +++ b/src/parsers/propertyParser.ts @@ -16,11 +16,12 @@ export function parseProperty( try { let type: ts.Type; + if (propertySignature) { - if (!propertySignature.type) { - type = checker.getAnyType(); - } else { + if (propertySignature.type) { type = checker.getTypeOfSymbolAtLocation(propertySymbol, propertySignature.type); + } else { + type = checker.getAnyType(); } } else { type = checker.getTypeOfSymbol(propertySymbol); @@ -36,7 +37,7 @@ export function parseProperty( parsedType = new IntrinsicNode('any'); isOptional = Boolean(propertySignature.questionToken); } else { - parsedType = resolveType(type, context, skipResolvingComplexTypes); + parsedType = resolveType(type, context, propertySignature?.type, skipResolvingComplexTypes); isOptional = Boolean(propertySymbol.flags & ts.SymbolFlags.Optional); } diff --git a/src/parsers/typeResolver.ts b/src/parsers/typeResolver.ts index 454be0d..828d034 100644 --- a/src/parsers/typeResolver.ts +++ b/src/parsers/typeResolver.ts @@ -20,6 +20,7 @@ import { export function resolveType( type: ts.Type, context: ParserContext, + typeNode?: ts.TypeNode, skipResolvingComplexTypes: boolean = false, ): TypeNode { const { checker, typeStack, includeExternalTypes } = context; @@ -36,7 +37,24 @@ export function resolveType( typeStack.push(typeId); } - const namespaces = getTypeNamespaces(type); + const typeNodeSymbol = + typeNode && ts.isTypeReferenceNode(typeNode) + ? checker.getSymbolAtLocation((typeNode as ts.TypeReferenceNode).typeName) + : undefined; + const declaredType = typeNodeSymbol ? checker.getDeclaredTypeOfSymbol(typeNodeSymbol) : undefined; + const namespaces = typeNodeSymbol + ? getTypeSymbolNamespaces(typeNodeSymbol) + : getTypeNamespaces(type); + + let typeSymbol: ts.Symbol | undefined; + if (typeNode && ts.isTypeReferenceNode(typeNode)) { + const typeNodeName = (typeNode as ts.TypeReferenceNode).typeName; + if (ts.isIdentifier(typeNodeName)) { + typeSymbol = checker.getSymbolAtLocation(typeNodeName); + } else if (ts.isQualifiedName(typeNodeName)) { + typeSymbol = checker.getSymbolAtLocation(typeNodeName.right); + } + } try { if (type.flags & ts.TypeFlags.TypeParameter && type.symbol) { @@ -58,13 +76,13 @@ export function resolveType( } if (!includeExternalTypes && isTypeExternal(type, checker)) { - const typeName = getTypeName(type, checker); + const typeName = getTypeName(type, typeSymbol, checker); // Fixes a weird TS behavior where it doesn't show the alias name but resolves to the actual type in case of RefCallback. if (typeName === 'bivarianceHack') { return new ReferenceNode('RefCallback', []); } - return new ReferenceNode(getTypeName(type, checker), namespaces); + return new ReferenceNode(getTypeName(type, typeSymbol, checker), namespaces); } if (hasFlag(type.flags, ts.TypeFlags.Boolean)) { @@ -91,7 +109,7 @@ export function resolveType( if (type.isUnion()) { const memberTypes: TypeNode[] = []; - const symbol = type.aliasSymbol ?? type.getSymbol(); + const symbol = typeSymbol ?? type.aliasSymbol ?? type.getSymbol(); let typeName = symbol?.getName(); if (typeName === '__type') { typeName = undefined; @@ -116,7 +134,7 @@ export function resolveType( if (type.isIntersection()) { const memberTypes: TypeNode[] = []; - const symbol = type.aliasSymbol ?? type.getSymbol(); + const symbol = typeSymbol ?? type.aliasSymbol ?? type.getSymbol(); let typeName = symbol?.getName(); if (typeName === '__type') { typeName = undefined; @@ -155,7 +173,7 @@ export function resolveType( if (checker.isTupleType(type)) { return new TupleNode( - type.aliasSymbol?.name, + typeSymbol?.name ?? type.aliasSymbol?.name, [], (type as ts.TupleType).typeArguments?.map((x) => resolveType(x, context)) ?? [], ); @@ -280,14 +298,17 @@ export function getTypeNamespaces(type: ts.Type): string[] { return []; } - if (symbol.name === '__function' || symbol.name === '__type') { + return getTypeSymbolNamespaces(symbol); +} + +function getTypeSymbolNamespaces(typeSymbol: ts.Symbol): string[] { + if (typeSymbol.name === '__function' || typeSymbol.name === '__type') { return []; } - const declaration = symbol.valueDeclaration ?? symbol.declarations?.[0]; + const declaration = typeSymbol.valueDeclaration ?? typeSymbol.declarations?.[0]; return getNodeNamespaces(declaration); } - export function getNodeNamespaces(node: ts.Node | undefined): string[] { if (!node) { return []; @@ -326,8 +347,12 @@ function isTypeExternal(type: ts.Type, checker: ts.TypeChecker): boolean { ); } -function getTypeName(type: ts.Type, checker: ts.TypeChecker): string { - const symbol = type.aliasSymbol ?? type.getSymbol(); +function getTypeName( + type: ts.Type, + typeSymbol: ts.Symbol | undefined, + checker: ts.TypeChecker, +): string { + const symbol = typeSymbol ?? type.aliasSymbol ?? type.getSymbol(); if (!symbol) { return checker.typeToString(type); } @@ -341,11 +366,11 @@ function getTypeName(type: ts.Type, checker: ts.TypeChecker): string { if ('target' in type) { typeArguments = checker .getTypeArguments(type as ts.TypeReference) - ?.map((x) => getTypeName(x, checker)); + ?.map((x) => getTypeName(x, undefined, checker)); } if (!typeArguments?.length) { - typeArguments = type.aliasTypeArguments?.map((x) => getTypeName(x, checker)) ?? []; + typeArguments = type.aliasTypeArguments?.map((x) => getTypeName(x, undefined, checker)) ?? []; } if (typeArguments && typeArguments.length > 0) { diff --git a/test/aliases/output.json b/test/aliases/output.json index 6805307..e7d5477 100644 --- a/test/aliases/output.json +++ b/test/aliases/output.json @@ -24,7 +24,7 @@ "value": 2 } ], - "name": "SomeType", + "name": "Alias", "parentNamespaces": [] }, "optional": false diff --git a/test/arrays/output.json b/test/arrays/output.json index 0f47648..021f956 100644 --- a/test/arrays/output.json +++ b/test/arrays/output.json @@ -62,6 +62,7 @@ "name": "undefined" } ], + "name": "Array", "parentNamespaces": [] }, "optional": true diff --git a/test/base-ui-component/output.json b/test/base-ui-component/output.json index 2d61309..7313bf4 100644 --- a/test/base-ui-component/output.json +++ b/test/base-ui-component/output.json @@ -85,7 +85,10 @@ "name": "undefined" } ], - "parentNamespaces": [] + "name": "RefObject", + "parentNamespaces": [ + "React" + ] }, "optional": true }, @@ -209,7 +212,7 @@ ] } ], - "name": "GenericHTMLProps", + "name": "Props", "parentNamespaces": [], "properties": [ { diff --git a/test/hook-arrow-function/output.json b/test/hook-arrow-function/output.json index 45f5385..de99ff0 100644 --- a/test/hook-arrow-function/output.json +++ b/test/hook-arrow-function/output.json @@ -109,7 +109,9 @@ "type": { "kind": "reference", "name": "RefCallback", - "parentNamespaces": [] + "parentNamespaces": [ + "React" + ] }, "optional": false } diff --git a/test/hook-function-expression/output.json b/test/hook-function-expression/output.json index d57dc9f..a324367 100644 --- a/test/hook-function-expression/output.json +++ b/test/hook-function-expression/output.json @@ -110,7 +110,9 @@ "type": { "kind": "reference", "name": "RefCallback", - "parentNamespaces": [] + "parentNamespaces": [ + "React" + ] }, "optional": false } diff --git a/test/hook-function/output.json b/test/hook-function/output.json index 90ddf70..aac3c46 100644 --- a/test/hook-function/output.json +++ b/test/hook-function/output.json @@ -110,7 +110,9 @@ "type": { "kind": "reference", "name": "RefCallback", - "parentNamespaces": [] + "parentNamespaces": [ + "React" + ] }, "optional": false } diff --git a/test/mui-overridable-component/output.json b/test/mui-overridable-component/output.json index c11ed51..6bc2f2f 100644 --- a/test/mui-overridable-component/output.json +++ b/test/mui-overridable-component/output.json @@ -100,7 +100,10 @@ "name": "undefined" } ], - "parentNamespaces": [] + "name": "CSSProperties", + "parentNamespaces": [ + "React" + ] }, "optional": true } diff --git a/test/namespaces/input.tsx b/test/namespaces/input.tsx index 1532b2b..73bd157 100644 --- a/test/namespaces/input.tsx +++ b/test/namespaces/input.tsx @@ -28,4 +28,6 @@ export namespace Root { export function fn3(params: Root.Sub.Params) {} -type OutsideType = 'one' | 'two' | 'three'; +export function fn4(a: Root.NamespacedType) {} + +type OutsideType = 'one' | 'two'; diff --git a/test/namespaces/output.json b/test/namespaces/output.json index 471181c..c2eed1d 100644 --- a/test/namespaces/output.json +++ b/test/namespaces/output.json @@ -55,15 +55,12 @@ "kind": "literal", "parentNamespaces": [], "value": "\"two\"" - }, - { - "kind": "literal", - "parentNamespaces": [], - "value": "\"three\"" } ], - "name": "OutsideType", - "parentNamespaces": [] + "name": "NamespacedType", + "parentNamespaces": [ + "Root" + ] }, "optional": false } @@ -82,6 +79,48 @@ ] } }, + { + "name": "fn4", + "type": { + "kind": "function", + "name": "fn4", + "parentNamespaces": [], + "callSignatures": [ + { + "parameters": [ + { + "type": { + "kind": "union", + "types": [ + { + "kind": "literal", + "parentNamespaces": [], + "value": "\"one\"" + }, + { + "kind": "literal", + "parentNamespaces": [], + "value": "\"two\"" + } + ], + "name": "NamespacedType", + "parentNamespaces": [ + "Root" + ] + }, + "name": "a", + "optional": false + } + ], + "returnValueType": { + "kind": "intrinsic", + "parentNamespaces": [], + "name": "void" + } + } + ] + } + }, { "name": "fn2", "type": { @@ -159,15 +198,12 @@ "kind": "literal", "parentNamespaces": [], "value": "\"two\"" - }, - { - "kind": "literal", - "parentNamespaces": [], - "value": "\"three\"" } ], - "name": "OutsideType", - "parentNamespaces": [] + "name": "NamespacedType", + "parentNamespaces": [ + "Root" + ] }, "optional": false } @@ -269,15 +305,12 @@ "kind": "literal", "parentNamespaces": [], "value": "\"two\"" - }, - { - "kind": "literal", - "parentNamespaces": [], - "value": "\"three\"" } ], - "name": "OutsideType", - "parentNamespaces": [] + "name": "NamespacedType", + "parentNamespaces": [ + "Root" + ] }, "optional": false } diff --git a/test/react-event-handlers/output.json b/test/react-event-handlers/output.json index 064ece5..8905f37 100644 --- a/test/react-event-handlers/output.json +++ b/test/react-event-handlers/output.json @@ -37,7 +37,10 @@ "name": "undefined" } ], - "parentNamespaces": [] + "name": "KeyboardEventHandler", + "parentNamespaces": [ + "React" + ] }, "optional": true }, @@ -70,7 +73,10 @@ "name": "undefined" } ], - "parentNamespaces": [] + "name": "FocusEventHandler", + "parentNamespaces": [ + "React" + ] }, "optional": true } diff --git a/test/react-refs/output.json b/test/react-refs/output.json index d62def8..ce1e759 100644 --- a/test/react-refs/output.json +++ b/test/react-refs/output.json @@ -24,7 +24,9 @@ "type": { "kind": "reference", "name": "RefCallback", - "parentNamespaces": [] + "parentNamespaces": [ + "React" + ] }, "optional": false }, @@ -57,7 +59,10 @@ "name": "undefined" } ], - "parentNamespaces": [] + "name": "RefObject", + "parentNamespaces": [ + "React" + ] }, "optional": true }, @@ -77,7 +82,10 @@ "name": "undefined" } ], - "parentNamespaces": [] + "name": "RefCallback", + "parentNamespaces": [ + "React" + ] }, "optional": true }, @@ -99,7 +107,10 @@ "name": "undefined" } ], - "parentNamespaces": [] + "name": "Ref", + "parentNamespaces": [ + "React" + ] }, "optional": true } diff --git a/test/ts-utility-types/output.json b/test/ts-utility-types/output.json index 9b7725d..87b60a1 100644 --- a/test/ts-utility-types/output.json +++ b/test/ts-utility-types/output.json @@ -131,6 +131,7 @@ { "type": { "kind": "tuple", + "name": "Parameters", "parentNamespaces": [], "types": [ { From 6748f09e7df85fd870762f5be1cf9f3166d68a28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dudak?= Date: Tue, 20 May 2025 11:39:32 +0200 Subject: [PATCH 03/10] Linting, more tests --- src/parsers/typeResolver.ts | 1 - test/aliases/input.ts | 4 +++ test/aliases/output.json | 60 +++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/parsers/typeResolver.ts b/src/parsers/typeResolver.ts index 828d034..ecf5e0d 100644 --- a/src/parsers/typeResolver.ts +++ b/src/parsers/typeResolver.ts @@ -41,7 +41,6 @@ export function resolveType( typeNode && ts.isTypeReferenceNode(typeNode) ? checker.getSymbolAtLocation((typeNode as ts.TypeReferenceNode).typeName) : undefined; - const declaredType = typeNodeSymbol ? checker.getDeclaredTypeOfSymbol(typeNodeSymbol) : undefined; const namespaces = typeNodeSymbol ? getTypeSymbolNamespaces(typeNodeSymbol) : getTypeNamespaces(type); diff --git a/test/aliases/input.ts b/test/aliases/input.ts index 9f7cfad..9cd318a 100644 --- a/test/aliases/input.ts +++ b/test/aliases/input.ts @@ -1,6 +1,10 @@ export interface A { a: Alias; + r: MyRecord; } +export function fn1(a: Alias, r: MyRecord) {} + type SomeType = 1 | 2; type Alias = SomeType; +type MyRecord = Record; diff --git a/test/aliases/output.json b/test/aliases/output.json index e7d5477..c8d189f 100644 --- a/test/aliases/output.json +++ b/test/aliases/output.json @@ -1,6 +1,56 @@ { "name": "test/aliases/input", "exports": [ + { + "name": "fn1", + "type": { + "kind": "function", + "name": "fn1", + "parentNamespaces": [], + "callSignatures": [ + { + "parameters": [ + { + "type": { + "kind": "union", + "types": [ + { + "kind": "literal", + "parentNamespaces": [], + "value": 1 + }, + { + "kind": "literal", + "parentNamespaces": [], + "value": 2 + } + ], + "name": "Alias", + "parentNamespaces": [] + }, + "name": "a", + "optional": false + }, + { + "type": { + "kind": "object", + "name": "MyRecord", + "parentNamespaces": [], + "properties": [] + }, + "name": "r", + "optional": false + } + ], + "returnValueType": { + "kind": "intrinsic", + "parentNamespaces": [], + "name": "void" + } + } + ] + } + }, { "name": "A", "type": { @@ -28,6 +78,16 @@ "parentNamespaces": [] }, "optional": false + }, + { + "name": "r", + "type": { + "kind": "object", + "name": "MyRecord", + "parentNamespaces": [], + "properties": [] + }, + "optional": false } ] } From 5d3470d132fbf84a9c2f38a2b36c17b8a27d0cc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dudak?= Date: Mon, 26 May 2025 14:15:58 +0200 Subject: [PATCH 04/10] Better handle identifiers types as type parameters --- src/parsers/propertyParser.ts | 29 +++- test/generics-closed/input.ts | 18 +++ test/generics-closed/output.json | 219 ++++++++++++++++++++++++++ test/react-event-handlers/output.json | 10 +- test/react-refs/output.json | 5 +- 5 files changed, 268 insertions(+), 13 deletions(-) create mode 100644 test/generics-closed/input.ts create mode 100644 test/generics-closed/output.json diff --git a/src/parsers/propertyParser.ts b/src/parsers/propertyParser.ts index f045c7b..75085da 100644 --- a/src/parsers/propertyParser.ts +++ b/src/parsers/propertyParser.ts @@ -37,7 +37,12 @@ export function parseProperty( parsedType = new IntrinsicNode('any'); isOptional = Boolean(propertySignature.questionToken); } else { - parsedType = resolveType(type, context, propertySignature?.type, skipResolvingComplexTypes); + parsedType = resolveType( + type, + context, + isTypeParameterLike(type) ? undefined : propertySignature?.type, + skipResolvingComplexTypes, + ); isOptional = Boolean(propertySymbol.flags & ts.SymbolFlags.Optional); } @@ -59,3 +64,25 @@ export function parseProperty( parsedSymbolStack.pop(); } } + +function isTypeParameterLike(type: ts.Type): boolean { + // Check if the type is a type parameter + return ( + (type.flags & ts.TypeFlags.TypeParameter) !== 0 || + ((type.flags & ts.TypeFlags.Union) !== 0 && isOptionalTypeParameter(type as ts.UnionType)) + ); +} + +function isOptionalTypeParameter(type: ts.UnionType): boolean { + // Check if the type is defined as + // foo?: T + // where T is a type parameter + + return ( + type.types.length === 2 && + type.types.some((t) => t.flags & ts.TypeFlags.Undefined) && + type.types.some( + (t) => 'objectFlags' in t && ((t.objectFlags as number) & ts.ObjectFlags.Instantiated) !== 0, + ) + ); +} diff --git a/test/generics-closed/input.ts b/test/generics-closed/input.ts new file mode 100644 index 0000000..e2b0e42 --- /dev/null +++ b/test/generics-closed/input.ts @@ -0,0 +1,18 @@ +type ComponentProps = { + className?: string | ((state: TState) => string); + render?: ComponentRenderFn; +}; + +export function fn(props: MyComponent.Props) {} + +type ComponentRenderFn = (props: Props, state: State) => React.ReactElement; + +export type GenericHTMLProps = React.HTMLAttributes & { ref?: React.Ref | undefined }; + +namespace MyComponent { + export interface State { + disabled: boolean; + } + + export interface Props extends ComponentProps {} +} diff --git a/test/generics-closed/output.json b/test/generics-closed/output.json new file mode 100644 index 0000000..f4cd3fe --- /dev/null +++ b/test/generics-closed/output.json @@ -0,0 +1,219 @@ +{ + "name": "test/generics-closed/input", + "exports": [ + { + "name": "fn", + "type": { + "kind": "function", + "name": "fn", + "parentNamespaces": [], + "callSignatures": [ + { + "parameters": [ + { + "type": { + "kind": "object", + "name": "Props", + "parentNamespaces": [ + "MyComponent" + ], + "properties": [ + { + "name": "className", + "type": { + "kind": "union", + "types": [ + { + "kind": "intrinsic", + "parentNamespaces": [], + "name": "string" + }, + { + "kind": "function", + "parentNamespaces": [], + "callSignatures": [ + { + "parameters": [ + { + "type": { + "kind": "object", + "name": "State", + "parentNamespaces": [ + "MyComponent" + ], + "properties": [ + { + "name": "disabled", + "type": { + "kind": "intrinsic", + "parentNamespaces": [], + "name": "boolean" + }, + "optional": false + } + ] + }, + "name": "state", + "optional": false + } + ], + "returnValueType": { + "kind": "intrinsic", + "parentNamespaces": [], + "name": "string" + } + } + ] + }, + { + "kind": "intrinsic", + "parentNamespaces": [], + "name": "undefined" + } + ], + "parentNamespaces": [] + }, + "optional": true + }, + { + "name": "render", + "type": { + "kind": "union", + "types": [ + { + "kind": "function", + "name": "ComponentRenderFn", + "parentNamespaces": [], + "callSignatures": [ + { + "parameters": [ + { + "type": { + "kind": "intersection", + "types": [ + { + "kind": "reference", + "name": "HTMLAttributes", + "parentNamespaces": [ + "React" + ] + }, + { + "kind": "object", + "parentNamespaces": [], + "properties": [ + { + "name": "ref", + "type": { + "kind": "union", + "types": [ + { + "kind": "reference", + "name": "Ref", + "parentNamespaces": [ + "React" + ] + }, + { + "kind": "intrinsic", + "parentNamespaces": [], + "name": "undefined" + } + ], + "parentNamespaces": [] + }, + "optional": true + } + ] + } + ], + "name": "Props", + "parentNamespaces": [], + "properties": [ + { + "name": "ref", + "type": { + "kind": "union", + "types": [ + { + "kind": "reference", + "name": "Ref", + "parentNamespaces": [ + "React" + ] + }, + { + "kind": "intrinsic", + "parentNamespaces": [], + "name": "undefined" + } + ], + "parentNamespaces": [] + }, + "optional": true + } + ] + }, + "name": "props", + "optional": false + }, + { + "type": { + "kind": "object", + "name": "State", + "parentNamespaces": [ + "MyComponent" + ], + "properties": [ + { + "name": "disabled", + "type": { + "kind": "intrinsic", + "parentNamespaces": [], + "name": "boolean" + }, + "optional": false + } + ] + }, + "name": "state", + "optional": false + } + ], + "returnValueType": { + "kind": "reference", + "name": "ReactElement>", + "parentNamespaces": [ + "React" + ] + } + } + ] + }, + { + "kind": "intrinsic", + "parentNamespaces": [], + "name": "undefined" + } + ], + "parentNamespaces": [] + }, + "optional": true + } + ] + }, + "name": "props", + "optional": false + } + ], + "returnValueType": { + "kind": "intrinsic", + "parentNamespaces": [], + "name": "void" + } + } + ] + } + } + ] +} \ No newline at end of file diff --git a/test/react-event-handlers/output.json b/test/react-event-handlers/output.json index 8905f37..064ece5 100644 --- a/test/react-event-handlers/output.json +++ b/test/react-event-handlers/output.json @@ -37,10 +37,7 @@ "name": "undefined" } ], - "name": "KeyboardEventHandler", - "parentNamespaces": [ - "React" - ] + "parentNamespaces": [] }, "optional": true }, @@ -73,10 +70,7 @@ "name": "undefined" } ], - "name": "FocusEventHandler", - "parentNamespaces": [ - "React" - ] + "parentNamespaces": [] }, "optional": true } diff --git a/test/react-refs/output.json b/test/react-refs/output.json index ce1e759..23600fb 100644 --- a/test/react-refs/output.json +++ b/test/react-refs/output.json @@ -82,10 +82,7 @@ "name": "undefined" } ], - "name": "RefCallback", - "parentNamespaces": [ - "React" - ] + "parentNamespaces": [] }, "optional": true }, From 9e5905c050c852f7376b60e6da8734e112347a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dudak?= Date: Wed, 11 Jun 2025 13:03:58 +0200 Subject: [PATCH 05/10] Revert invalid test result --- test/base-ui-component/output.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/base-ui-component/output.json b/test/base-ui-component/output.json index 7313bf4..cd32db4 100644 --- a/test/base-ui-component/output.json +++ b/test/base-ui-component/output.json @@ -212,7 +212,7 @@ ] } ], - "name": "Props", + "name": "GenericHTMLProps", "parentNamespaces": [], "properties": [ { From e8611e7628194872ceabddc50395ce740fadffbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dudak?= Date: Wed, 11 Jun 2025 14:56:29 +0200 Subject: [PATCH 06/10] Do not preserve type parameter references --- src/parsers/typeResolver.ts | 22 ++++++-- test/generics-closed-parameters/input.ts | 9 ++++ test/generics-closed-parameters/output.json | 56 +++++++++++++++++++++ test/generics-closed/output.json | 2 +- 4 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 test/generics-closed-parameters/input.ts create mode 100644 test/generics-closed-parameters/output.json diff --git a/src/parsers/typeResolver.ts b/src/parsers/typeResolver.ts index 67f630a..b5bfff3 100644 --- a/src/parsers/typeResolver.ts +++ b/src/parsers/typeResolver.ts @@ -45,13 +45,27 @@ export function resolveType( ? getTypeSymbolNamespaces(typeNodeSymbol) : getTypeNamespaces(type); + // The following code handles cases where the type is a simple alias of another type (type Alias = SomeType). + // TypeScript resolves the alias automatically, but we want to preserve the original type symbol if it exists. + // + // However, this also covers cases where the type is a type parameter (as in `type Generic = { value: T }`). + // Here we don't want to preserve T as a type symbol, but rather resolve it to its actual type. let typeSymbol: ts.Symbol | undefined; if (typeNode && ts.isTypeReferenceNode(typeNode)) { const typeNodeName = (typeNode as ts.TypeReferenceNode).typeName; - if (ts.isIdentifier(typeNodeName)) { - typeSymbol = checker.getSymbolAtLocation(typeNodeName); - } else if (ts.isQualifiedName(typeNodeName)) { - typeSymbol = checker.getSymbolAtLocation(typeNodeName.right); + if (ts.isIdentifier(typeNodeName) && typeNodeName.text !== type.aliasSymbol?.name) { + const typeSymbolCandidate = checker.getSymbolAtLocation(typeNodeName); + if (typeSymbolCandidate && !(typeSymbolCandidate.flags & ts.SymbolFlags.TypeParameter)) { + typeSymbol = typeSymbolCandidate; + } + } else if ( + ts.isQualifiedName(typeNodeName) && + typeNodeName.right.text !== type.aliasSymbol?.name + ) { + const typeSymbolCandidate = checker.getSymbolAtLocation(typeNodeName.right); + if (typeSymbolCandidate && !(typeSymbolCandidate.flags & ts.SymbolFlags.TypeParameter)) { + typeSymbol = typeSymbolCandidate; + } } } diff --git a/test/generics-closed-parameters/input.ts b/test/generics-closed-parameters/input.ts new file mode 100644 index 0000000..a3bf78b --- /dev/null +++ b/test/generics-closed-parameters/input.ts @@ -0,0 +1,9 @@ +export function test(a: GenericObject) { + return null; +} + +type GenericObject = { + x: T; +}; + +type MyUnion = string | number; diff --git a/test/generics-closed-parameters/output.json b/test/generics-closed-parameters/output.json new file mode 100644 index 0000000..17f3498 --- /dev/null +++ b/test/generics-closed-parameters/output.json @@ -0,0 +1,56 @@ +{ + "name": "test/generics-closed-parameters/input", + "exports": [ + { + "name": "test", + "type": { + "kind": "function", + "name": "test", + "parentNamespaces": [], + "callSignatures": [ + { + "parameters": [ + { + "type": { + "kind": "object", + "name": "GenericObject", + "parentNamespaces": [], + "properties": [ + { + "name": "x", + "type": { + "kind": "union", + "types": [ + { + "kind": "intrinsic", + "parentNamespaces": [], + "name": "string" + }, + { + "kind": "intrinsic", + "parentNamespaces": [], + "name": "number" + } + ], + "name": "MyUnion", + "parentNamespaces": [] + }, + "optional": false + } + ] + }, + "name": "a", + "optional": false + } + ], + "returnValueType": { + "kind": "intrinsic", + "parentNamespaces": [], + "name": "null" + } + } + ] + } + } + ] +} \ No newline at end of file diff --git a/test/generics-closed/output.json b/test/generics-closed/output.json index f4cd3fe..d1052ee 100644 --- a/test/generics-closed/output.json +++ b/test/generics-closed/output.json @@ -127,7 +127,7 @@ ] } ], - "name": "Props", + "name": "GenericHTMLProps", "parentNamespaces": [], "properties": [ { From 2a7b241fcad6936cb8fceb1865814cdf1b71b0eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dudak?= Date: Wed, 11 Jun 2025 15:38:43 +0200 Subject: [PATCH 07/10] More fixes --- src/parsers/typeResolver.ts | 10 +- test/base-ui-component/input.tsx | 47 ++++--- test/base-ui-component/output.json | 193 +++++++++++++++++++++++++++-- 3 files changed, 213 insertions(+), 37 deletions(-) diff --git a/src/parsers/typeResolver.ts b/src/parsers/typeResolver.ts index b5bfff3..2861154 100644 --- a/src/parsers/typeResolver.ts +++ b/src/parsers/typeResolver.ts @@ -37,14 +37,6 @@ export function resolveType( typeStack.push(typeId); } - const typeNodeSymbol = - typeNode && ts.isTypeReferenceNode(typeNode) - ? checker.getSymbolAtLocation((typeNode as ts.TypeReferenceNode).typeName) - : undefined; - const namespaces = typeNodeSymbol - ? getTypeSymbolNamespaces(typeNodeSymbol) - : getTypeNamespaces(type); - // The following code handles cases where the type is a simple alias of another type (type Alias = SomeType). // TypeScript resolves the alias automatically, but we want to preserve the original type symbol if it exists. // @@ -69,6 +61,8 @@ export function resolveType( } } + const namespaces = typeSymbol ? getTypeSymbolNamespaces(typeSymbol) : getTypeNamespaces(type); + try { if (type.flags & ts.TypeFlags.TypeParameter && type.symbol) { const declaration = type.symbol.declarations?.[0] as ts.TypeParameterDeclaration | undefined; diff --git a/test/base-ui-component/input.tsx b/test/base-ui-component/input.tsx index d8dd0b8..c86bc7d 100644 --- a/test/base-ui-component/input.tsx +++ b/test/base-ui-component/input.tsx @@ -1,13 +1,13 @@ import * as React from 'react'; -export const BaseUIComponent = React.forwardRef(function BaseUIComponent( - props: BaseUIComponent.Props, +export const BaseUIComponent1 = React.forwardRef(function BaseUIComponent( + props: BaseUIComponent1.Props, ref: React.ForwardedRef, ) { return
; }); -export namespace BaseUIComponent { +export namespace BaseUIComponent1 { export interface Props extends BaseUIComponentProps<'div', State> { value?: string; onValueChange?: (value: string) => void; @@ -23,36 +23,47 @@ export namespace BaseUIComponent { } } +export const BaseUIComponent2 = React.forwardRef(function BaseUIComponent( + props: BaseUIComponent2.Props, + ref: React.ForwardedRef, +) { + return
; +}); + +export namespace BaseUIComponent2 { + export interface Props extends BaseUIComponentProps<'div', State> {} + + export interface State {} +} + type BaseUIComponentProps< ElementType extends React.ElementType, State, - RenderFunctionProps = GenericHTMLProps, -> = Omit>, 'className'> & { - /** - * CSS class applied to the element, or a function that - * returns a class based on the component’s state. - */ + RenderFunctionProps = HTMLProps, +> = Omit< + WithBaseUIEvent>, + 'className' | 'color' | 'defaultValue' | 'defaultChecked' +> & { className?: string | ((state: State) => string); - /** - * Allows you to replace the component’s HTML element - * with a different tag, or compose it with another component. - * - * Accepts a `ReactElement` or a function that returns the element to render. - */ render?: | ComponentRenderFn | React.ReactElement>; }; -type ComponentRenderFn = (props: Props, state: State) => React.ReactElement; +export type ComponentRenderFn = ( + props: Props, + state: State, +) => React.ReactElement; type WithBaseUIEvent = { [K in keyof T]: WithPreventBaseUIHandler; }; -export type GenericHTMLProps = React.HTMLAttributes & { ref?: React.Ref | undefined }; +type HTMLProps = React.HTMLAttributes & { + ref?: React.Ref | undefined; +}; -export type BaseUIEvent> = E & { +type BaseUIEvent> = E & { preventBaseUIHandler: () => void; readonly baseUIHandlerPrevented?: boolean; }; diff --git a/test/base-ui-component/output.json b/test/base-ui-component/output.json index cd32db4..3d9824a 100644 --- a/test/base-ui-component/output.json +++ b/test/base-ui-component/output.json @@ -2,7 +2,7 @@ "name": "test/base-ui-component/input", "exports": [ { - "name": "BaseUIComponent", + "name": "BaseUIComponent1", "type": { "kind": "component", "name": "ForwardRefExoticComponent", @@ -113,7 +113,7 @@ "kind": "object", "name": "State", "parentNamespaces": [ - "BaseUIComponent" + "BaseUIComponent1" ], "properties": [ { @@ -147,10 +147,6 @@ ], "parentNamespaces": [] }, - "documentation": { - "description": "CSS class applied to the element, or a function that\nreturns a class based on the component’s state.", - "tags": [] - }, "optional": true }, { @@ -212,7 +208,7 @@ ] } ], - "name": "GenericHTMLProps", + "name": "HTMLProps", "parentNamespaces": [], "properties": [ { @@ -247,7 +243,7 @@ "kind": "object", "name": "State", "parentNamespaces": [ - "BaseUIComponent" + "BaseUIComponent1" ], "properties": [ { @@ -283,9 +279,184 @@ ], "parentNamespaces": [] }, - "documentation": { - "description": "Allows you to replace the component’s HTML element\nwith a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render.", - "tags": [] + "optional": true + } + ] + } + }, + { + "name": "BaseUIComponent2", + "type": { + "kind": "component", + "name": "ForwardRefExoticComponent", + "parentNamespaces": [], + "props": [ + { + "name": "className", + "type": { + "kind": "union", + "types": [ + { + "kind": "intrinsic", + "parentNamespaces": [], + "name": "string" + }, + { + "kind": "function", + "parentNamespaces": [], + "callSignatures": [ + { + "parameters": [ + { + "type": { + "kind": "object", + "name": "State", + "parentNamespaces": [ + "BaseUIComponent2" + ], + "properties": [] + }, + "name": "state", + "optional": false + } + ], + "returnValueType": { + "kind": "intrinsic", + "parentNamespaces": [], + "name": "string" + } + } + ] + }, + { + "kind": "intrinsic", + "parentNamespaces": [], + "name": "undefined" + } + ], + "parentNamespaces": [] + }, + "optional": true + }, + { + "name": "render", + "type": { + "kind": "union", + "types": [ + { + "kind": "reference", + "name": "ReactElement, string | JSXElementConstructor>", + "parentNamespaces": [ + "React" + ] + }, + { + "kind": "function", + "name": "ComponentRenderFn", + "parentNamespaces": [], + "callSignatures": [ + { + "parameters": [ + { + "type": { + "kind": "intersection", + "types": [ + { + "kind": "reference", + "name": "HTMLAttributes", + "parentNamespaces": [ + "React" + ] + }, + { + "kind": "object", + "parentNamespaces": [], + "properties": [ + { + "name": "ref", + "type": { + "kind": "union", + "types": [ + { + "kind": "reference", + "name": "Ref", + "parentNamespaces": [ + "React" + ] + }, + { + "kind": "intrinsic", + "parentNamespaces": [], + "name": "undefined" + } + ], + "parentNamespaces": [] + }, + "optional": true + } + ] + } + ], + "name": "HTMLProps", + "parentNamespaces": [], + "properties": [ + { + "name": "ref", + "type": { + "kind": "union", + "types": [ + { + "kind": "reference", + "name": "Ref", + "parentNamespaces": [ + "React" + ] + }, + { + "kind": "intrinsic", + "parentNamespaces": [], + "name": "undefined" + } + ], + "parentNamespaces": [] + }, + "optional": true + } + ] + }, + "name": "props", + "optional": false + }, + { + "type": { + "kind": "object", + "name": "State", + "parentNamespaces": [ + "BaseUIComponent2" + ], + "properties": [] + }, + "name": "state", + "optional": false + } + ], + "returnValueType": { + "kind": "reference", + "name": "ReactElement>", + "parentNamespaces": [ + "React" + ] + } + } + ] + }, + { + "kind": "intrinsic", + "parentNamespaces": [], + "name": "undefined" + } + ], + "parentNamespaces": [] }, "optional": true } From 045c05559f9460e7e57493c9f2031ebbbacdf9c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dudak?= Date: Wed, 11 Jun 2025 19:08:13 +0200 Subject: [PATCH 08/10] Fix handling of non-generic aliases that point to generic types --- src/parsers/objectParser.ts | 10 ++-- src/parsers/typeResolver.ts | 55 ++++++++++----------- test/aliases/input.ts | 4 +- test/aliases/output.json | 24 ++++++++- test/base-ui-component/output.json | 4 +- test/conditional-types/output.json | 2 +- test/generics-closed-parameters/output.json | 2 +- test/generics/output.json | 6 +-- test/mapped-types/output.json | 8 +-- test/object-types/output.json | 2 + test/ts-utility-types/output.json | 6 +-- 11 files changed, 71 insertions(+), 52 deletions(-) diff --git a/src/parsers/objectParser.ts b/src/parsers/objectParser.ts index 42895cf..2fafdf2 100644 --- a/src/parsers/objectParser.ts +++ b/src/parsers/objectParser.ts @@ -2,24 +2,20 @@ import ts from 'typescript'; import { parseProperty } from './propertyParser'; import { ParserContext } from '../parser'; import { ObjectNode } from '../models'; -import { getTypeNamespaces } from './typeResolver'; +import { getTypeName, getTypeNamespaces } from './typeResolver'; export function parseObjectType( type: ts.Type, context: ParserContext, skipResolvingComplexTypes: boolean, ): ObjectNode | undefined { - const { shouldInclude, shouldResolveObject, typeStack, includeExternalTypes } = context; + const { shouldInclude, shouldResolveObject, typeStack, includeExternalTypes, checker } = context; const properties = type .getProperties() .filter((property) => includeExternalTypes || !isPropertyExternal(property)); - const typeSymbol = type.aliasSymbol ?? type.getSymbol(); - let typeName = typeSymbol?.getName(); - if (typeName === '__type') { - typeName = undefined; - } + const typeName = getTypeName(type, undefined, checker, false); if (properties.length) { if ( diff --git a/src/parsers/typeResolver.ts b/src/parsers/typeResolver.ts index 2861154..9a768a2 100644 --- a/src/parsers/typeResolver.ts +++ b/src/parsers/typeResolver.ts @@ -89,7 +89,10 @@ export function resolveType( return new ReferenceNode('RefCallback', []); } - return new ReferenceNode(getTypeName(type, typeSymbol, checker), namespaces); + return new ReferenceNode( + getTypeName(type, typeSymbol, checker) ?? checker.typeToString(type), + namespaces, + ); } if (hasFlag(type.flags, ts.TypeFlags.Boolean)) { @@ -116,11 +119,7 @@ export function resolveType( if (type.isUnion()) { const memberTypes: TypeNode[] = []; - const symbol = typeSymbol ?? type.aliasSymbol ?? type.getSymbol(); - let typeName = symbol?.getName(); - if (typeName === '__type') { - typeName = undefined; - } + const typeName = getTypeName(type, typeSymbol, checker, false); // @ts-expect-error - Internal API if (type.origin?.isUnion()) { @@ -141,11 +140,7 @@ export function resolveType( if (type.isIntersection()) { const memberTypes: TypeNode[] = []; - const symbol = typeSymbol ?? type.aliasSymbol ?? type.getSymbol(); - let typeName = symbol?.getName(); - if (typeName === '__type') { - typeName = undefined; - } + const typeName = getTypeName(type, typeSymbol, checker, false); for (const memberType of type.types) { memberTypes.push(resolveType(memberType, context)); @@ -246,12 +241,7 @@ export function resolveType( type.flags & ts.TypeFlags.Object || (type.flags & ts.TypeFlags.NonPrimitive && checker.typeToString(type) === 'object') ) { - const typeSymbol = type.aliasSymbol ?? type.getSymbol(); - let typeName = typeSymbol?.getName(); - if (typeName === '__type') { - typeName = undefined; - } - + const typeName = getTypeName(type, typeSymbol, checker, false); return new ObjectNode(typeName, namespaces, [], undefined); } @@ -358,30 +348,39 @@ function isTypeExternal(type: ts.Type, checker: ts.TypeChecker): boolean { ); } -function getTypeName( +export function getTypeName( type: ts.Type, typeSymbol: ts.Symbol | undefined, checker: ts.TypeChecker, -): string { + useFallback: boolean = true, +): string | undefined { const symbol = typeSymbol ?? type.aliasSymbol ?? type.getSymbol(); if (!symbol) { - return checker.typeToString(type); + return useFallback ? checker.typeToString(type) : undefined; } const typeName = symbol.getName(); if (typeName === '__type') { - return checker.typeToString(type); + return useFallback ? checker.typeToString(type) : undefined; } let typeArguments: string[] | undefined; - if ('target' in type) { - typeArguments = checker - .getTypeArguments(type as ts.TypeReference) - ?.map((x) => getTypeName(x, undefined, checker)); - } - if (!typeArguments?.length) { - typeArguments = type.aliasTypeArguments?.map((x) => getTypeName(x, undefined, checker)) ?? []; + if (type.aliasSymbol && !type.aliasTypeArguments) { + typeArguments = []; + } else { + if ('target' in type) { + typeArguments = checker + .getTypeArguments(type as ts.TypeReference) + ?.map((x) => getTypeName(x, undefined, checker, true) ?? 'unknown'); + } + + if (!typeArguments?.length) { + typeArguments = + type.aliasTypeArguments?.map( + (x) => getTypeName(x, undefined, checker, true) ?? 'unknown', + ) ?? []; + } } if (typeArguments && typeArguments.length > 0) { diff --git a/test/aliases/input.ts b/test/aliases/input.ts index 9cd318a..565658d 100644 --- a/test/aliases/input.ts +++ b/test/aliases/input.ts @@ -1,10 +1,12 @@ export interface A { a: Alias; r: MyRecord; + s: AliasToGeneric; } -export function fn1(a: Alias, r: MyRecord) {} +export function fn1(a: Alias, r: MyRecord, s: AliasToGeneric) {} type SomeType = 1 | 2; type Alias = SomeType; type MyRecord = Record; +type AliasToGeneric = Set; diff --git a/test/aliases/output.json b/test/aliases/output.json index c8d189f..7d39a14 100644 --- a/test/aliases/output.json +++ b/test/aliases/output.json @@ -34,12 +34,22 @@ { "type": { "kind": "object", - "name": "MyRecord", + "name": "MyRecord", "parentNamespaces": [], "properties": [] }, "name": "r", "optional": false + }, + { + "type": { + "kind": "object", + "name": "AliasToGeneric", + "parentNamespaces": [], + "properties": [] + }, + "name": "s", + "optional": false } ], "returnValueType": { @@ -83,7 +93,17 @@ "name": "r", "type": { "kind": "object", - "name": "MyRecord", + "name": "MyRecord", + "parentNamespaces": [], + "properties": [] + }, + "optional": false + }, + { + "name": "s", + "type": { + "kind": "object", + "name": "AliasToGeneric", "parentNamespaces": [], "properties": [] }, diff --git a/test/base-ui-component/output.json b/test/base-ui-component/output.json index 3d9824a..6e65434 100644 --- a/test/base-ui-component/output.json +++ b/test/base-ui-component/output.json @@ -208,7 +208,7 @@ ] } ], - "name": "HTMLProps", + "name": "HTMLProps", "parentNamespaces": [], "properties": [ { @@ -397,7 +397,7 @@ ] } ], - "name": "HTMLProps", + "name": "HTMLProps", "parentNamespaces": [], "properties": [ { diff --git a/test/conditional-types/output.json b/test/conditional-types/output.json index b04e5b5..d45a6ad 100644 --- a/test/conditional-types/output.json +++ b/test/conditional-types/output.json @@ -53,7 +53,7 @@ { "type": { "kind": "object", - "name": "Props", + "name": "Props", "parentNamespaces": [], "properties": [ { diff --git a/test/generics-closed-parameters/output.json b/test/generics-closed-parameters/output.json index 17f3498..26506a9 100644 --- a/test/generics-closed-parameters/output.json +++ b/test/generics-closed-parameters/output.json @@ -13,7 +13,7 @@ { "type": { "kind": "object", - "name": "GenericObject", + "name": "GenericObject", "parentNamespaces": [], "properties": [ { diff --git a/test/generics/output.json b/test/generics/output.json index 1c5f5ca..0c87994 100644 --- a/test/generics/output.json +++ b/test/generics/output.json @@ -13,7 +13,7 @@ { "type": { "kind": "object", - "name": "GenericFunctionParameters", + "name": "GenericFunctionParameters", "parentNamespaces": [], "properties": [ { @@ -30,14 +30,14 @@ "name": "nestedGenericType", "type": { "kind": "object", - "name": "GenericInterface", + "name": "GenericInterface>", "parentNamespaces": [], "properties": [ { "name": "data", "type": { "kind": "object", - "name": "GenericInterface", + "name": "GenericInterface", "parentNamespaces": [], "properties": [ { diff --git a/test/mapped-types/output.json b/test/mapped-types/output.json index 1d4e217..3a8f1ff 100644 --- a/test/mapped-types/output.json +++ b/test/mapped-types/output.json @@ -13,7 +13,7 @@ { "type": { "kind": "object", - "name": "Partial", + "name": "Partial", "parentNamespaces": [], "properties": [ { @@ -83,7 +83,7 @@ { "type": { "kind": "object", - "name": "Required", + "name": "Required", "parentNamespaces": [], "properties": [ { @@ -131,7 +131,7 @@ { "type": { "kind": "object", - "name": "Partial", + "name": "Partial", "parentNamespaces": [], "properties": [ { @@ -201,7 +201,7 @@ { "type": { "kind": "object", - "name": "Required", + "name": "Required", "parentNamespaces": [], "properties": [ { diff --git a/test/object-types/output.json b/test/object-types/output.json index 899c124..26c320f 100644 --- a/test/object-types/output.json +++ b/test/object-types/output.json @@ -79,6 +79,7 @@ { "type": { "kind": "object", + "name": "ObjectKeyword", "parentNamespaces": [], "properties": [] }, @@ -153,6 +154,7 @@ "name": "objectKeyword", "type": { "kind": "object", + "name": "ObjectKeyword", "parentNamespaces": [], "properties": [] }, diff --git a/test/ts-utility-types/output.json b/test/ts-utility-types/output.json index 87b60a1..0fbaeff 100644 --- a/test/ts-utility-types/output.json +++ b/test/ts-utility-types/output.json @@ -13,7 +13,7 @@ { "type": { "kind": "object", - "name": "Pick", + "name": "Pick", "parentNamespaces": [], "properties": [ { @@ -72,7 +72,7 @@ { "type": { "kind": "object", - "name": "Omit", + "name": "Omit", "parentNamespaces": [], "properties": [ { @@ -136,7 +136,7 @@ "types": [ { "kind": "object", - "name": "Pick", + "name": "Pick", "parentNamespaces": [], "properties": [ { From 43ccbe5837e9e8cd20db286c1d05f4fad17addea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dudak?= Date: Wed, 11 Jun 2025 19:37:25 +0200 Subject: [PATCH 09/10] Fix display of React refs --- src/models/node.ts | 2 +- src/models/types/intersection.ts | 4 +- src/models/types/union.ts | 4 +- src/parsers/typeResolver.ts | 9 +- test/aliases/output.json | 8 +- test/arrays/output.json | 7 +- test/base-ui-component/output.json | 38 +++------ .../output.json | 3 +- test/component-function-variable/output.json | 18 ++-- test/conditional-types/output.json | 6 +- test/forwardRef/output.json | 6 +- test/function-optional-parameters/output.json | 3 +- test/generics-closed-parameters/output.json | 4 +- test/generics-closed/output.json | 14 ++-- test/hook-multiple-parameters/output.json | 4 +- test/interfaces/output.json | 3 +- test/intersection-types/output.json | 21 ++--- test/jsdoc-extra-tags/output.json | 6 +- test/jsdocs/output.json | 12 +-- test/literal-unions/output.json | 82 ++++++++----------- test/mapped-types/output.json | 12 +-- test/memo/output.json | 6 +- test/mui-overridable-component/output.json | 13 +-- test/namespaces/output.json | 16 ++-- test/object-types/output.json | 2 - test/props-optional/output.json | 9 +- test/props-with-callbacks/output.json | 6 +- test/react-event-handlers/output.json | 6 +- test/react-refs/output.json | 44 +--------- test/reference-types/output.json | 3 +- test/ts-utility-types/output.json | 9 +- test/union-types/output.json | 12 +-- 32 files changed, 134 insertions(+), 258 deletions(-) diff --git a/src/models/node.ts b/src/models/node.ts index 787adb7..7e5ec35 100644 --- a/src/models/node.ts +++ b/src/models/node.ts @@ -1,5 +1,5 @@ export interface TypeNode { readonly kind: string; name: string | undefined; - parentNamespaces: string[]; + parentNamespaces: string[] | undefined; } diff --git a/src/models/types/intersection.ts b/src/models/types/intersection.ts index 1fa759b..532f189 100644 --- a/src/models/types/intersection.ts +++ b/src/models/types/intersection.ts @@ -7,14 +7,16 @@ export class IntersectionNode implements TypeNode { constructor( public name: string | undefined, - public parentNamespaces: string[], + parentNamespaces: string[], types: TypeNode[], public properties: PropertyNode[], ) { const flatTypes = flattenTypes(types, IntersectionNode); sortMemberTypes(flatTypes); this.types = deduplicateMemberTypes(flatTypes); + this.parentNamespaces = name ? parentNamespaces : undefined; } types: readonly TypeNode[]; + parentNamespaces: string[] | undefined; } diff --git a/src/models/types/union.ts b/src/models/types/union.ts index d338f5c..b4a7851 100644 --- a/src/models/types/union.ts +++ b/src/models/types/union.ts @@ -8,16 +8,18 @@ export class UnionNode implements TypeNode { constructor( public name: string | undefined, - public parentNamespaces: string[], + parentNamespaces: string[], types: TypeNode[], ) { const flatTypes = flattenTypes(types, UnionNode); sanitizeBooleanLiterals(flatTypes); sortMemberTypes(flatTypes); this.types = deduplicateMemberTypes(flatTypes); + this.parentNamespaces = name ? parentNamespaces : undefined; } types: readonly TypeNode[]; + parentNamespaces: string[] | undefined; } /** diff --git a/src/parsers/typeResolver.ts b/src/parsers/typeResolver.ts index 9a768a2..97a8d86 100644 --- a/src/parsers/typeResolver.ts +++ b/src/parsers/typeResolver.ts @@ -89,10 +89,7 @@ export function resolveType( return new ReferenceNode('RefCallback', []); } - return new ReferenceNode( - getTypeName(type, typeSymbol, checker) ?? checker.typeToString(type), - namespaces, - ); + return new ReferenceNode(typeName ?? checker.typeToString(type), namespaces); } if (hasFlag(type.flags, ts.TypeFlags.Boolean)) { @@ -359,6 +356,10 @@ export function getTypeName( return useFallback ? checker.typeToString(type) : undefined; } + if (typeSymbol && !type.aliasSymbol && !type.symbol) { + return useFallback ? checker.typeToString(type) : undefined; + } + const typeName = symbol.getName(); if (typeName === '__type') { return useFallback ? checker.typeToString(type) : undefined; diff --git a/test/aliases/output.json b/test/aliases/output.json index 7d39a14..f47cd70 100644 --- a/test/aliases/output.json +++ b/test/aliases/output.json @@ -25,8 +25,8 @@ "value": 2 } ], - "name": "Alias", - "parentNamespaces": [] + "parentNamespaces": [], + "name": "Alias" }, "name": "a", "optional": false @@ -84,8 +84,8 @@ "value": 2 } ], - "name": "Alias", - "parentNamespaces": [] + "parentNamespaces": [], + "name": "Alias" }, "optional": false }, diff --git a/test/arrays/output.json b/test/arrays/output.json index 021f956..9cac120 100644 --- a/test/arrays/output.json +++ b/test/arrays/output.json @@ -61,9 +61,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "name": "Array", - "parentNamespaces": [] + ] }, "optional": true }, @@ -86,8 +84,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } diff --git a/test/base-ui-component/output.json b/test/base-ui-component/output.json index 6e65434..2d5009a 100644 --- a/test/base-ui-component/output.json +++ b/test/base-ui-component/output.json @@ -23,8 +23,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -62,8 +61,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -84,10 +82,6 @@ "parentNamespaces": [], "name": "undefined" } - ], - "name": "RefObject", - "parentNamespaces": [ - "React" ] }, "optional": true @@ -144,8 +138,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -200,16 +193,15 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } ] } ], - "name": "HTMLProps", "parentNamespaces": [], + "name": "HTMLProps", "properties": [ { "name": "ref", @@ -228,8 +220,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } @@ -276,8 +267,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } @@ -333,8 +323,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -389,16 +378,15 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } ] } ], - "name": "HTMLProps", "parentNamespaces": [], + "name": "HTMLProps", "properties": [ { "name": "ref", @@ -417,8 +405,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } @@ -455,8 +442,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } diff --git a/test/component-function-declaration/output.json b/test/component-function-declaration/output.json index d842d4f..1dda460 100644 --- a/test/component-function-declaration/output.json +++ b/test/component-function-declaration/output.json @@ -23,8 +23,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } diff --git a/test/component-function-variable/output.json b/test/component-function-variable/output.json index b9a32c2..185a1e7 100644 --- a/test/component-function-variable/output.json +++ b/test/component-function-variable/output.json @@ -23,8 +23,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -43,8 +42,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } @@ -73,8 +71,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -93,8 +90,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } @@ -123,8 +119,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -143,8 +138,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } diff --git a/test/conditional-types/output.json b/test/conditional-types/output.json index d45a6ad..d128810 100644 --- a/test/conditional-types/output.json +++ b/test/conditional-types/output.json @@ -34,8 +34,7 @@ "parentNamespaces": [], "name": "null" } - ], - "parentNamespaces": [] + ] } } ] @@ -80,8 +79,7 @@ "parentNamespaces": [], "name": "null" } - ], - "parentNamespaces": [] + ] }, "optional": false } diff --git a/test/forwardRef/output.json b/test/forwardRef/output.json index 5affaea..88f9290 100644 --- a/test/forwardRef/output.json +++ b/test/forwardRef/output.json @@ -23,8 +23,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -43,8 +42,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } diff --git a/test/function-optional-parameters/output.json b/test/function-optional-parameters/output.json index 7315951..689c485 100644 --- a/test/function-optional-parameters/output.json +++ b/test/function-optional-parameters/output.json @@ -33,8 +33,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "name": "optional", "documentation": { diff --git a/test/generics-closed-parameters/output.json b/test/generics-closed-parameters/output.json index 26506a9..8247328 100644 --- a/test/generics-closed-parameters/output.json +++ b/test/generics-closed-parameters/output.json @@ -32,8 +32,8 @@ "name": "number" } ], - "name": "MyUnion", - "parentNamespaces": [] + "parentNamespaces": [], + "name": "MyUnion" }, "optional": false } diff --git a/test/generics-closed/output.json b/test/generics-closed/output.json index d1052ee..8cde6d6 100644 --- a/test/generics-closed/output.json +++ b/test/generics-closed/output.json @@ -70,8 +70,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -119,16 +118,15 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } ] } ], - "name": "GenericHTMLProps", "parentNamespaces": [], + "name": "GenericHTMLProps", "properties": [ { "name": "ref", @@ -147,8 +145,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } @@ -195,8 +192,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } diff --git a/test/hook-multiple-parameters/output.json b/test/hook-multiple-parameters/output.json index fdb944e..14b20e6 100644 --- a/test/hook-multiple-parameters/output.json +++ b/test/hook-multiple-parameters/output.json @@ -70,8 +70,8 @@ "value": "\"high\"" } ], - "name": "Severity", - "parentNamespaces": [] + "parentNamespaces": [], + "name": "Severity" }, "name": "severity", "documentation": { diff --git a/test/interfaces/output.json b/test/interfaces/output.json index ea8bec6..69192e8 100644 --- a/test/interfaces/output.json +++ b/test/interfaces/output.json @@ -98,8 +98,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } diff --git a/test/intersection-types/output.json b/test/intersection-types/output.json index 43c54e0..7c72557 100644 --- a/test/intersection-types/output.json +++ b/test/intersection-types/output.json @@ -59,8 +59,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -79,15 +78,13 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } ] } ], - "parentNamespaces": [], "properties": [ { "name": "a", @@ -122,8 +119,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } @@ -200,8 +196,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -220,16 +215,15 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } ] } ], - "name": "AB", "parentNamespaces": [], + "name": "AB", "properties": [ { "name": "a", @@ -264,8 +258,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } diff --git a/test/jsdoc-extra-tags/output.json b/test/jsdoc-extra-tags/output.json index 9fd2250..7c0664c 100644 --- a/test/jsdoc-extra-tags/output.json +++ b/test/jsdoc-extra-tags/output.json @@ -75,8 +75,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "name": "reason", "documentation": { @@ -116,8 +115,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "documentation": { "description": "For internal use only", diff --git a/test/jsdocs/output.json b/test/jsdocs/output.json index 7a0ced8..0ce780f 100644 --- a/test/jsdocs/output.json +++ b/test/jsdocs/output.json @@ -23,8 +23,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "documentation": { "description": "The class name.\nUsed to make the component more classy.", @@ -47,8 +46,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "documentation": { "description": "A value.", @@ -101,8 +99,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "name": "p2", "documentation": { @@ -147,8 +144,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "name": "p2", "documentation": { diff --git a/test/literal-unions/output.json b/test/literal-unions/output.json index 56e4d73..a600da7 100644 --- a/test/literal-unions/output.json +++ b/test/literal-unions/output.json @@ -36,8 +36,7 @@ "parentNamespaces": [], "value": "\"baz\"" } - ], - "parentNamespaces": [] + ] }, "optional": false }, @@ -61,8 +60,7 @@ "parentNamespaces": [], "value": 3 } - ], - "parentNamespaces": [] + ] }, "optional": false }, @@ -87,8 +85,8 @@ "value": "\"baz\"" } ], - "name": "StringUnion", - "parentNamespaces": [] + "parentNamespaces": [], + "name": "StringUnion" }, "optional": false }, @@ -113,8 +111,8 @@ "value": 3 } ], - "name": "NumberUnion", - "parentNamespaces": [] + "parentNamespaces": [], + "name": "NumberUnion" }, "optional": false }, @@ -149,16 +147,15 @@ "value": "\"baz\"" } ], - "name": "StringUnion", - "parentNamespaces": [] + "parentNamespaces": [], + "name": "StringUnion" }, { "kind": "intrinsic", "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "name": "ref", "optional": false @@ -198,8 +195,8 @@ "value": "\"baz\"" } ], - "name": "StringUnion", - "parentNamespaces": [] + "parentNamespaces": [], + "name": "StringUnion" }, { "kind": "union", @@ -220,11 +217,10 @@ "value": 3 } ], - "name": "NumberUnion", - "parentNamespaces": [] + "parentNamespaces": [], + "name": "NumberUnion" } - ], - "parentNamespaces": [] + ] }, "optional": false }, @@ -255,8 +251,8 @@ "value": "\"baz\"" } ], - "name": "StringUnion", - "parentNamespaces": [] + "parentNamespaces": [], + "name": "StringUnion" }, { "kind": "literal", @@ -264,16 +260,15 @@ "value": "\"qux\"" } ], - "name": "IndirectStringUnion", - "parentNamespaces": [] + "parentNamespaces": [], + "name": "IndirectStringUnion" }, { "kind": "intrinsic", "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": false } @@ -320,8 +315,7 @@ "parentNamespaces": [], "value": "\"baz\"" } - ], - "parentNamespaces": [] + ] }, "name": "inlineStringUnion", "optional": false @@ -345,8 +339,7 @@ "parentNamespaces": [], "value": 3 } - ], - "parentNamespaces": [] + ] }, "name": "inlineNumberUnion", "optional": false @@ -371,8 +364,8 @@ "value": "\"baz\"" } ], - "name": "StringUnion", - "parentNamespaces": [] + "parentNamespaces": [], + "name": "StringUnion" }, "name": "referencedStringUnion", "optional": false @@ -397,8 +390,8 @@ "value": 3 } ], - "name": "NumberUnion", - "parentNamespaces": [] + "parentNamespaces": [], + "name": "NumberUnion" }, "name": "referencedNumberUnion", "optional": false @@ -426,8 +419,8 @@ "value": "\"baz\"" } ], - "name": "StringUnion", - "parentNamespaces": [] + "parentNamespaces": [], + "name": "StringUnion" }, { "kind": "union", @@ -448,11 +441,10 @@ "value": 3 } ], - "name": "NumberUnion", - "parentNamespaces": [] + "parentNamespaces": [], + "name": "NumberUnion" } - ], - "parentNamespaces": [] + ] }, "name": "unionOfUnions", "optional": false @@ -483,8 +475,8 @@ "value": "\"baz\"" } ], - "name": "StringUnion", - "parentNamespaces": [] + "parentNamespaces": [], + "name": "StringUnion" }, { "kind": "literal", @@ -492,16 +484,15 @@ "value": "\"qux\"" } ], - "name": "IndirectStringUnion", - "parentNamespaces": [] + "parentNamespaces": [], + "name": "IndirectStringUnion" }, { "kind": "intrinsic", "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "name": "indirectUnion", "optional": false @@ -564,8 +555,7 @@ "parentNamespaces": [], "value": "\"indirectUnion\"" } - ], - "parentNamespaces": [] + ] }, "name": "prop", "optional": false diff --git a/test/mapped-types/output.json b/test/mapped-types/output.json index 3a8f1ff..63515b2 100644 --- a/test/mapped-types/output.json +++ b/test/mapped-types/output.json @@ -31,8 +31,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -51,8 +50,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } @@ -149,8 +147,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -169,8 +166,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } diff --git a/test/memo/output.json b/test/memo/output.json index 07a2bc5..a836287 100644 --- a/test/memo/output.json +++ b/test/memo/output.json @@ -23,8 +23,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -43,8 +42,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } diff --git a/test/mui-overridable-component/output.json b/test/mui-overridable-component/output.json index 6bc2f2f..c41dc2d 100644 --- a/test/mui-overridable-component/output.json +++ b/test/mui-overridable-component/output.json @@ -23,8 +23,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "documentation": { "description": "The component used for the root node.", @@ -57,8 +56,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -77,8 +75,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -99,10 +96,6 @@ "parentNamespaces": [], "name": "undefined" } - ], - "name": "CSSProperties", - "parentNamespaces": [ - "React" ] }, "optional": true diff --git a/test/namespaces/output.json b/test/namespaces/output.json index c2eed1d..4fbc136 100644 --- a/test/namespaces/output.json +++ b/test/namespaces/output.json @@ -57,10 +57,10 @@ "value": "\"two\"" } ], - "name": "NamespacedType", "parentNamespaces": [ "Root" - ] + ], + "name": "NamespacedType" }, "optional": false } @@ -103,10 +103,10 @@ "value": "\"two\"" } ], - "name": "NamespacedType", "parentNamespaces": [ "Root" - ] + ], + "name": "NamespacedType" }, "name": "a", "optional": false @@ -200,10 +200,10 @@ "value": "\"two\"" } ], - "name": "NamespacedType", "parentNamespaces": [ "Root" - ] + ], + "name": "NamespacedType" }, "optional": false } @@ -307,10 +307,10 @@ "value": "\"two\"" } ], - "name": "NamespacedType", "parentNamespaces": [ "Root" - ] + ], + "name": "NamespacedType" }, "optional": false } diff --git a/test/object-types/output.json b/test/object-types/output.json index 26c320f..899c124 100644 --- a/test/object-types/output.json +++ b/test/object-types/output.json @@ -79,7 +79,6 @@ { "type": { "kind": "object", - "name": "ObjectKeyword", "parentNamespaces": [], "properties": [] }, @@ -154,7 +153,6 @@ "name": "objectKeyword", "type": { "kind": "object", - "name": "ObjectKeyword", "parentNamespaces": [], "properties": [] }, diff --git a/test/props-optional/output.json b/test/props-optional/output.json index 273cad1..8eff7b2 100644 --- a/test/props-optional/output.json +++ b/test/props-optional/output.json @@ -32,8 +32,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -52,8 +51,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": false }, @@ -81,8 +79,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } diff --git a/test/props-with-callbacks/output.json b/test/props-with-callbacks/output.json index da8c2ac..08c322b 100644 --- a/test/props-with-callbacks/output.json +++ b/test/props-with-callbacks/output.json @@ -41,8 +41,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "name": "reason", "optional": false @@ -140,8 +139,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } diff --git a/test/react-event-handlers/output.json b/test/react-event-handlers/output.json index 064ece5..1915c0d 100644 --- a/test/react-event-handlers/output.json +++ b/test/react-event-handlers/output.json @@ -36,8 +36,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -69,8 +68,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } diff --git a/test/react-refs/output.json b/test/react-refs/output.json index 23600fb..f67e131 100644 --- a/test/react-refs/output.json +++ b/test/react-refs/output.json @@ -8,39 +8,6 @@ "name": "Test", "parentNamespaces": [], "props": [ - { - "name": "refObject", - "type": { - "kind": "reference", - "name": "RefObject", - "parentNamespaces": [ - "React" - ] - }, - "optional": false - }, - { - "name": "refCallback", - "type": { - "kind": "reference", - "name": "RefCallback", - "parentNamespaces": [ - "React" - ] - }, - "optional": false - }, - { - "name": "ref", - "type": { - "kind": "reference", - "name": "Ref", - "parentNamespaces": [ - "React" - ] - }, - "optional": false - }, { "name": "optionalRefObject", "type": { @@ -58,10 +25,6 @@ "parentNamespaces": [], "name": "undefined" } - ], - "name": "RefObject", - "parentNamespaces": [ - "React" ] }, "optional": true @@ -81,8 +44,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -103,10 +65,6 @@ "parentNamespaces": [], "name": "undefined" } - ], - "name": "Ref", - "parentNamespaces": [ - "React" ] }, "optional": true diff --git a/test/reference-types/output.json b/test/reference-types/output.json index 9071499..28ae860 100644 --- a/test/reference-types/output.json +++ b/test/reference-types/output.json @@ -86,8 +86,7 @@ } ] } - ], - "parentNamespaces": [] + ] } } ] diff --git a/test/ts-utility-types/output.json b/test/ts-utility-types/output.json index 0fbaeff..bcd55d2 100644 --- a/test/ts-utility-types/output.json +++ b/test/ts-utility-types/output.json @@ -40,8 +40,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } @@ -99,8 +98,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } @@ -163,8 +161,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } diff --git a/test/union-types/output.json b/test/union-types/output.json index 40e0431..e343f19 100644 --- a/test/union-types/output.json +++ b/test/union-types/output.json @@ -32,8 +32,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -52,8 +51,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true }, @@ -72,8 +70,7 @@ "parentNamespaces": [], "name": "number" } - ], - "parentNamespaces": [] + ] }, "optional": false }, @@ -92,8 +89,7 @@ "parentNamespaces": [], "name": "undefined" } - ], - "parentNamespaces": [] + ] }, "optional": true } From 197585219c7c151fa3f7b0ef19b74784851f93ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dudak?= Date: Wed, 11 Jun 2025 19:38:47 +0200 Subject: [PATCH 10/10] Fix the test --- test/react-refs/output.json | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/test/react-refs/output.json b/test/react-refs/output.json index f67e131..02743ed 100644 --- a/test/react-refs/output.json +++ b/test/react-refs/output.json @@ -8,6 +8,39 @@ "name": "Test", "parentNamespaces": [], "props": [ + { + "name": "refObject", + "type": { + "kind": "reference", + "name": "RefObject", + "parentNamespaces": [ + "React" + ] + }, + "optional": false + }, + { + "name": "refCallback", + "type": { + "kind": "reference", + "name": "RefCallback", + "parentNamespaces": [ + "React" + ] + }, + "optional": false + }, + { + "name": "ref", + "type": { + "kind": "reference", + "name": "Ref", + "parentNamespaces": [ + "React" + ] + }, + "optional": false + }, { "name": "optionalRefObject", "type": {