Skip to content

Commit 53ecaef

Browse files
committed
Simplify logic for getting a type name
1 parent ff4eb89 commit 53ecaef

File tree

3 files changed

+48
-32
lines changed

3 files changed

+48
-32
lines changed

src/parsers/common.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export function getTypeName(
1111
return useFallback ? checker.typeToString(type) : undefined;
1212
}
1313

14-
if (typeSymbol && !type.aliasSymbol && !type.symbol) {
14+
if (typeSymbol && !type.aliasSymbol && !type.symbol && !isAnyOrUnknown(type)) {
1515
return useFallback ? checker.typeToString(type) : undefined;
1616
}
1717

@@ -45,3 +45,7 @@ export function getTypeName(
4545

4646
return typeName;
4747
}
48+
49+
function isAnyOrUnknown(type: ts.Type): boolean {
50+
return (type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) > 0;
51+
}

src/parsers/typeResolver.ts

Lines changed: 43 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -46,32 +46,7 @@ export function resolveType(
4646
typeStack.push(typeId);
4747
}
4848

49-
// The following code handles cases where the type is a simple alias of another type (type Alias = SomeType).
50-
// TypeScript resolves the alias automatically, but we want to preserve the original type symbol if it exists.
51-
//
52-
// However, this also covers cases where the type is a type parameter (as in `type Generic<T> = { value: T }`).
53-
// Here we don't want to preserve T as a type symbol, but rather resolve it to its actual type.
54-
let typeSymbol: ts.Symbol | undefined;
55-
if (typeNode && ts.isTypeReferenceNode(typeNode)) {
56-
const typeNodeName = (typeNode as ts.TypeReferenceNode).typeName;
57-
let typeSymbolCandidate: ts.Symbol | undefined;
58-
if (ts.isIdentifier(typeNodeName)) {
59-
typeSymbolCandidate = checker.getSymbolAtLocation(typeNodeName);
60-
} else if (ts.isQualifiedName(typeNodeName)) {
61-
typeSymbolCandidate = checker.getSymbolAtLocation(typeNodeName.right);
62-
}
63-
64-
if (
65-
typeSymbolCandidate &&
66-
!areEquivalent(typeNodeName, type, checker) &&
67-
!(typeSymbolCandidate.flags & ts.SymbolFlags.TypeParameter)
68-
) {
69-
typeSymbol = typeSymbolCandidate;
70-
}
71-
}
72-
73-
const typeName = getTypeName(type, typeSymbol, checker);
74-
const namespaces = typeSymbol ? getTypeSymbolNamespaces(typeSymbol) : getTypeNamespaces(type);
49+
const { name: typeName, namespaces } = getFullyQualifiedName(type, typeNode, checker);
7550

7651
try {
7752
if (type.flags & ts.TypeFlags.TypeParameter && type.symbol) {
@@ -171,7 +146,7 @@ export function resolveType(
171146

172147
if (checker.isTupleType(type)) {
173148
return new TupleNode(
174-
typeSymbol?.name ?? type.aliasSymbol?.name,
149+
typeName,
175150
namespaces,
176151
(type as ts.TupleType).typeArguments?.map((x) => resolveType(x, undefined, context)) ?? [],
177152
);
@@ -194,11 +169,11 @@ export function resolveType(
194169
}
195170

196171
if (type.flags & ts.TypeFlags.Any) {
197-
return new IntrinsicNode('any', typeSymbol?.name ?? type.aliasSymbol?.name, namespaces);
172+
return new IntrinsicNode('any', typeName, namespaces);
198173
}
199174

200175
if (type.flags & ts.TypeFlags.Unknown) {
201-
return new IntrinsicNode('unknown', typeSymbol?.name ?? type.aliasSymbol?.name, namespaces);
176+
return new IntrinsicNode('unknown', typeName, namespaces);
202177
}
203178

204179
if (type.flags & ts.TypeFlags.Literal) {
@@ -263,7 +238,7 @@ export function resolveType(
263238
`Unable to handle a type with flag "${ts.TypeFlags[type.flags]}". Using any instead.`,
264239
);
265240

266-
return new IntrinsicNode('any', typeSymbol?.name ?? type.aliasSymbol?.name, namespaces);
241+
return new IntrinsicNode('any', typeName, namespaces);
267242
} finally {
268243
typeStack.pop();
269244
}
@@ -289,6 +264,44 @@ const allowedBuiltInReactTypes = new Set([
289264
'React.ForwardRefExoticComponent',
290265
]);
291266

267+
function getFullyQualifiedName(
268+
type: ts.Type,
269+
typeNode: ts.TypeNode | undefined,
270+
checker: ts.TypeChecker,
271+
) {
272+
// The following code handles cases where the type is a simple alias of another type (type Alias = SomeType).
273+
// TypeScript resolves the alias automatically, but we want to preserve the original type symbol if it exists.
274+
//
275+
// However, this also covers cases where the type is a type parameter (as in `type Generic<T> = { value: T }`).
276+
// Here we don't want to preserve T as a type symbol, but rather resolve it to its actual type.
277+
let typeSymbol: ts.Symbol | undefined;
278+
if (typeNode && ts.isTypeReferenceNode(typeNode)) {
279+
const typeNodeName = (typeNode as ts.TypeReferenceNode).typeName;
280+
let typeSymbolCandidate: ts.Symbol | undefined;
281+
if (ts.isIdentifier(typeNodeName)) {
282+
typeSymbolCandidate = checker.getSymbolAtLocation(typeNodeName);
283+
} else if (ts.isQualifiedName(typeNodeName)) {
284+
typeSymbolCandidate = checker.getSymbolAtLocation(typeNodeName.right);
285+
}
286+
287+
if (
288+
typeSymbolCandidate &&
289+
!areEquivalent(typeNodeName, type, checker) &&
290+
!(typeSymbolCandidate.flags & ts.SymbolFlags.TypeParameter)
291+
) {
292+
typeSymbol = typeSymbolCandidate;
293+
}
294+
}
295+
296+
const name = getTypeName(type, typeSymbol, checker);
297+
const namespaces = typeSymbol ? getTypeSymbolNamespaces(typeSymbol) : getTypeNamespaces(type);
298+
299+
return {
300+
name,
301+
namespaces,
302+
};
303+
}
304+
292305
export function getTypeNamespaces(type: ts.Type): string[] {
293306
const symbol = type.aliasSymbol ?? type.getSymbol();
294307
if (!symbol) {

test/ts-utility-types/output.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@
129129
{
130130
"type": {
131131
"kind": "tuple",
132-
"name": "Parameters",
133132
"parentNamespaces": [],
134133
"types": [
135134
{

0 commit comments

Comments
 (0)