Skip to content

Commit 672f9e8

Browse files
committed
Determine if a type parameter is equal to default
1 parent b7c9793 commit 672f9e8

File tree

25 files changed

+3637
-3037
lines changed

25 files changed

+3637
-3037
lines changed

src/models/typeName.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import { AnyType } from './node';
33
export class TypeName {
44
public readonly name: string;
55
public readonly namespaces: readonly string[] | undefined;
6-
public readonly typeArguments: readonly AnyType[] | undefined;
6+
public readonly typeArguments: readonly TypeArgument[] | undefined;
77

88
constructor(
99
name: string,
1010
namespaces: readonly string[] | undefined = undefined,
11-
typeArguments: readonly AnyType[] | undefined = undefined,
11+
typeArguments: readonly TypeArgument[] | undefined = undefined,
1212
) {
1313
this.name = name;
1414
this.namespaces = namespaces;
@@ -24,14 +24,19 @@ export class TypeName {
2424
}
2525
}
2626

27+
export interface TypeArgument {
28+
type: AnyType;
29+
equalToDefault: boolean;
30+
}
31+
2732
function formatNameWithTypeArguments(
2833
name: string,
29-
typeArguments: readonly AnyType[] | undefined,
34+
typeArguments: readonly TypeArgument[] | undefined,
3035
): string {
3136
if (!typeArguments || typeArguments.length === 0) {
3237
return name;
3338
}
3439

35-
const args = typeArguments.map((arg) => arg.toString()).join(', ');
40+
const args = typeArguments.map((arg) => arg.type.toString()).join(', ');
3641
return `${name}<${args}>`;
3742
}

src/parsers/common.ts

Lines changed: 35 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import ts from 'typescript';
2-
import { TypeName } from '../models/typeName';
3-
import { AnyType } from '../models';
2+
import { TypeArgument, TypeName } from '../models/typeName';
43
import { resolveType } from './typeResolver';
54
import { ParserContext } from '../parser';
65

@@ -35,7 +34,7 @@ export function getFullName(
3534
}
3635
}
3736

38-
const name = getTypeName(type, typeSymbol, checker);
37+
const name = getTypeName(type, typeSymbol);
3938
const namespaces = typeSymbol ? getTypeSymbolNamespaces(typeSymbol) : getTypeNamespaces(type);
4039
const typeArguments = getTypeArguments(type, context);
4140

@@ -83,11 +82,7 @@ function getTypeSymbolNamespaces(typeSymbol: ts.Symbol): string[] {
8382
return namespaces;
8483
}
8584

86-
function getTypeName(
87-
type: ts.Type,
88-
typeSymbol: ts.Symbol | undefined,
89-
checker: ts.TypeChecker,
90-
): string | undefined {
85+
function getTypeName(type: ts.Type, typeSymbol: ts.Symbol | undefined): string | undefined {
9186
const symbol = typeSymbol ?? type.aliasSymbol ?? type.getSymbol();
9287
if (!symbol) {
9388
return undefined;
@@ -102,39 +97,50 @@ function getTypeName(
10297
return undefined;
10398
}
10499

105-
// If all generic arguments are default, omit them entirely
106-
if (areAllGenericArgumentsSameAsDefault(type, checker)) {
107-
return typeName;
108-
}
109-
110100
return typeName;
111101
}
112102

113-
function getTypeArguments(type: ts.Type, context: ParserContext): AnyType[] {
114-
let typeArguments: AnyType[] = [];
103+
function getTypeArguments(type: ts.Type, context: ParserContext): TypeArgument[] {
104+
let typeArguments: TypeArgument[] = [];
115105

116106
if (type.aliasSymbol && !type.aliasTypeArguments) {
117107
typeArguments = [];
118108
} else {
119109
if ('target' in type) {
120-
typeArguments = context.checker.getTypeArguments(type as ts.TypeReference)?.map((x) => {
121-
return resolveType(x, undefined, context);
122-
});
110+
typeArguments = context.checker
111+
.getTypeArguments(type as ts.TypeReference)
112+
?.map((arg, index) => {
113+
const parameterType = resolveType(arg, undefined, context);
114+
const equalToDefault = isGenericArgumentsSameAsDefault(type, index, context.checker);
115+
116+
return {
117+
type: parameterType,
118+
equalToDefault,
119+
} satisfies TypeArgument;
120+
});
123121
}
124122

125123
if (!typeArguments.length) {
126124
typeArguments =
127-
type.aliasTypeArguments?.map((x) => {
128-
return resolveType(x, undefined, context);
125+
type.aliasTypeArguments?.map((arg, index) => {
126+
const parameterType = resolveType(arg, undefined, context);
127+
resolveType(arg, undefined, context);
128+
const equalToDefault = isGenericArgumentsSameAsDefault(type, index, context.checker);
129+
130+
return {
131+
type: parameterType,
132+
equalToDefault,
133+
} satisfies TypeArgument;
129134
}) ?? [];
130135
}
131136
}
132137

133138
return typeArguments;
134139
}
135140

136-
function areAllGenericArgumentsSameAsDefault(
141+
function isGenericArgumentsSameAsDefault(
137142
type: ts.Type, // The instantiated type, e.g., Props<string>
143+
argumentIndex: number,
138144
checker: ts.TypeChecker,
139145
): boolean {
140146
let typeArguments: readonly ts.Type[] | undefined;
@@ -176,19 +182,17 @@ function areAllGenericArgumentsSameAsDefault(
176182
return false;
177183
}
178184

179-
for (let i = 0; i < typeArguments.length; i++) {
180-
const argumentType = typeArguments[i];
181-
const typeParameterDeclaration = typeParameters[i];
185+
const argumentType = typeArguments[argumentIndex];
186+
const typeParameterDeclaration = typeParameters[argumentIndex];
182187

183-
if (!typeParameterDeclaration.default) {
184-
return false; // Argument provided for a parameter without a default
185-
}
188+
if (!typeParameterDeclaration.default) {
189+
return false; // Argument provided for a parameter without a default
190+
}
186191

187-
const defaultType = checker.getTypeFromTypeNode(typeParameterDeclaration.default);
192+
const defaultType = checker.getTypeFromTypeNode(typeParameterDeclaration.default);
188193

189-
if (argumentType !== defaultType) {
190-
return false;
191-
}
194+
if (argumentType !== defaultType) {
195+
return false;
192196
}
193197

194198
return true;

test/aliases/output.json

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,18 @@
3838
"name": "MyRecord",
3939
"typeArguments": [
4040
{
41-
"kind": "intrinsic",
42-
"intrinsic": "string"
41+
"type": {
42+
"kind": "intrinsic",
43+
"intrinsic": "string"
44+
},
45+
"equalToDefault": false
4346
},
4447
{
45-
"kind": "intrinsic",
46-
"intrinsic": "string"
48+
"type": {
49+
"kind": "intrinsic",
50+
"intrinsic": "string"
51+
},
52+
"equalToDefault": false
4753
}
4854
]
4955
},
@@ -124,12 +130,18 @@
124130
"name": "MyRecord",
125131
"typeArguments": [
126132
{
127-
"kind": "intrinsic",
128-
"intrinsic": "string"
133+
"type": {
134+
"kind": "intrinsic",
135+
"intrinsic": "string"
136+
},
137+
"equalToDefault": false
129138
},
130139
{
131-
"kind": "intrinsic",
132-
"intrinsic": "string"
140+
"type": {
141+
"kind": "intrinsic",
142+
"intrinsic": "string"
143+
},
144+
"equalToDefault": false
133145
}
134146
]
135147
},
@@ -231,12 +243,18 @@
231243
"name": "MyRecord",
232244
"typeArguments": [
233245
{
234-
"kind": "intrinsic",
235-
"intrinsic": "string"
246+
"type": {
247+
"kind": "intrinsic",
248+
"intrinsic": "string"
249+
},
250+
"equalToDefault": false
236251
},
237252
{
238-
"kind": "intrinsic",
239-
"intrinsic": "string"
253+
"type": {
254+
"kind": "intrinsic",
255+
"intrinsic": "string"
256+
},
257+
"equalToDefault": false
240258
}
241259
]
242260
},
@@ -317,12 +335,18 @@
317335
"name": "MyRecord",
318336
"typeArguments": [
319337
{
320-
"kind": "intrinsic",
321-
"intrinsic": "string"
338+
"type": {
339+
"kind": "intrinsic",
340+
"intrinsic": "string"
341+
},
342+
"equalToDefault": false
322343
},
323344
{
324-
"kind": "intrinsic",
325-
"intrinsic": "string"
345+
"type": {
346+
"kind": "intrinsic",
347+
"intrinsic": "string"
348+
},
349+
"equalToDefault": false
326350
}
327351
]
328352
},

0 commit comments

Comments
 (0)