Skip to content

Commit facf5fc

Browse files
committed
Fix generics not being generated for interfaces
1 parent 0f8d3c8 commit facf5fc

11 files changed

Lines changed: 353 additions & 158 deletions

lib/generate/Generator.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { ClassIndexer } from '../parse/ClassIndexer';
88
import { ClassLoader } from '../parse/ClassLoader';
99
import { CommentLoader } from '../parse/CommentLoader';
1010
import { ConstructorLoader } from '../parse/ConstructorLoader';
11+
import { GenericsLoader } from '../parse/GenericsLoader';
1112
import type { PackageMetadata } from '../parse/PackageMetadataLoader';
1213
import { PackageMetadataLoader } from '../parse/PackageMetadataLoader';
1314
import { ParameterLoader } from '../parse/ParameterLoader';
@@ -82,6 +83,10 @@ export class Generator {
8283
const constructorsUnresolved = new ConstructorLoader({ commentLoader }).getConstructors(classAndInterfaceIndex);
8384
const constructors = await parameterResolver.resolveAllConstructorParameters(constructorsUnresolved);
8485

86+
// Load generics data
87+
const genericsUnresolved = new GenericsLoader({ parameterLoader }).getGenerics(classAndInterfaceIndex);
88+
const generics = await parameterResolver.resolveAllGenericTypeParameterData(genericsUnresolved);
89+
8590
// Load extensions data
8691
const extensionsUnresolved = parameterLoader.loadAllExtensionData(classAndInterfaceIndex);
8792
const extensions = await parameterResolver.resolveAllExtensionData(extensionsUnresolved, classAndInterfaceIndex);
@@ -106,8 +111,9 @@ export class Generator {
106111
contextConstructor,
107112
pathDestination,
108113
classAndInterfaceIndex,
109-
classExtensions: extensions,
110114
classConstructors: constructors,
115+
classGenerics: generics,
116+
classExtensions: extensions,
111117
externalComponents,
112118
contextParser: new ContextParser({
113119
documentLoader: new PrefetchedDocumentLoader({

lib/parse/ConstructorLoader.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { AST, TSESTreeOptions } from '@typescript-eslint/typescript-estree'
33
import { AST_NODE_TYPES } from '@typescript-eslint/typescript-estree';
44
import type { ClassIndex, ClassLoaded, ClassReferenceLoaded, GenericallyTyped } from './ClassIndex';
55
import type { CommentLoader } from './CommentLoader';
6-
import type { GenericTypeParameterData, ParameterDataField, ParameterRangeUnresolved } from './ParameterLoader';
6+
import type { ParameterDataField, ParameterRangeUnresolved } from './ParameterLoader';
77
import { ParameterLoader } from './ParameterLoader';
88

99
/**
@@ -27,7 +27,6 @@ export class ConstructorLoader {
2727
for (const [ className, classLoadedRoot ] of Object.entries(classIndex)) {
2828
// Initialize default value
2929
constructorDataIndex[className] = {
30-
genericTypeParameters: [],
3130
parameters: [],
3231
classLoaded: classLoadedRoot,
3332
};
@@ -132,7 +131,6 @@ export interface ConstructorLoaderArgs {
132131
* Constructor parameter information
133132
*/
134133
export interface ConstructorData<R> {
135-
genericTypeParameters: GenericTypeParameterData<R>[];
136134
parameters: ParameterDataField<R>[];
137135
classLoaded: ClassReferenceLoaded;
138136
}

lib/parse/GenericsLoader.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import type { ClassIndex, ClassReferenceLoaded } from './ClassIndex';
2+
import type {
3+
GenericTypeParameterData,
4+
ParameterLoader,
5+
ParameterRangeUnresolved,
6+
} from './ParameterLoader';
7+
8+
/**
9+
* Loads the generics data of classes.
10+
*/
11+
export class GenericsLoader {
12+
private readonly parameterLoader: ParameterLoader;
13+
14+
public constructor(args: GenericsLoaderArgs) {
15+
this.parameterLoader = args.parameterLoader;
16+
}
17+
18+
/**
19+
* Create a class index containing all generics data from the classes in the given index.
20+
* @param classIndex An index of loaded classes.
21+
*/
22+
public getGenerics(
23+
classIndex: ClassIndex<ClassReferenceLoaded>,
24+
): ClassIndex<GenericsData<ParameterRangeUnresolved>> {
25+
const genericsDataIndex: ClassIndex<GenericsData<ParameterRangeUnresolved>> = {};
26+
for (const [ className, classLoadedRoot ] of Object.entries(classIndex)) {
27+
if (classLoadedRoot.type === 'class' || classLoadedRoot.type === 'interface') {
28+
genericsDataIndex[className] = this.parameterLoader.loadClassGenerics(classLoadedRoot);
29+
}
30+
}
31+
return genericsDataIndex;
32+
}
33+
}
34+
35+
export interface GenericsLoaderArgs {
36+
parameterLoader: ParameterLoader;
37+
}
38+
39+
/**
40+
* Generics parameter information
41+
*/
42+
export interface GenericsData<R> {
43+
genericTypeParameters: GenericTypeParameterData<R>[];
44+
classLoaded: ClassReferenceLoaded;
45+
}

lib/parse/ParameterLoader.ts

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type { ClassReferenceLoaded, InterfaceLoaded, ClassReference,
1212
ClassReferenceLoadedClassOrInterface, ClassIndex } from './ClassIndex';
1313
import type { CommentData, ConstructorCommentData, CommentLoader } from './CommentLoader';
1414
import type { ConstructorData, ConstructorHolder } from './ConstructorLoader';
15+
import type { GenericsData } from './GenericsLoader';
1516
import type { TypeReferenceOverride } from './typereferenceoverride/TypeReferenceOverride';
1617
import { TypeReferenceOverrideAliasRecord } from './typereferenceoverride/TypeReferenceOverrideAliasRecord';
1718

@@ -100,43 +101,50 @@ export class ParameterLoader {
100101
// Load the constructor comment
101102
const constructorCommentData = this.commentLoader.getCommentDataFromConstructor(constructorChain);
102103

104+
// Load all constructor parameters
105+
const parameters: ParameterDataField<ParameterRangeUnresolved>[] = [];
106+
for (const field of constructorChain[0].constructor.value.params) {
107+
this.loadConstructorField(classLoaded, parameters, constructorCommentData, field);
108+
}
109+
110+
return {
111+
parameters,
112+
classLoaded,
113+
};
114+
}
115+
116+
/**
117+
* Load generics types from the given class.
118+
* @param classLoaded A loaded class.
119+
*/
120+
public loadClassGenerics(classLoaded: ClassReferenceLoadedClassOrInterface): GenericsData<ParameterRangeUnresolved> {
103121
// Load all generic type parameters
104122
const genericTypeParameters: GenericTypeParameterData<ParameterRangeUnresolved>[] = [];
105123
for (const [ genericName, genericType ] of Object.entries(classLoaded.generics)) {
106-
this.loadConstructorGeneric(
124+
this.loadClassGeneric(
107125
classLoaded,
108126
genericTypeParameters,
109-
constructorCommentData,
110127
genericName,
111128
genericType.type,
112129
);
113130
}
114131

115-
// Load all constructor parameters
116-
const parameters: ParameterDataField<ParameterRangeUnresolved>[] = [];
117-
for (const field of constructorChain[0].constructor.value.params) {
118-
this.loadConstructorField(classLoaded, parameters, constructorCommentData, field);
119-
}
120-
121132
return {
122133
genericTypeParameters,
123-
parameters,
124134
classLoaded,
125135
};
126136
}
127137

128138
/**
129-
* Load the generic type parameter data from the given generic in a constructor.
139+
* Load the generic type parameter data from the given generic in a class.
130140
* @param classLoaded The loaded class in which the field is defined.
131141
* @param genericTypeParameters The array of generic type parameters that will be appended to.
132-
* @param constructorCommentData Comment data from the constructor.
133142
* @param genericName The generic type name.
134143
* @param genericType The optional generic type range.
135144
*/
136-
public loadConstructorGeneric(
145+
public loadClassGeneric(
137146
classLoaded: ClassReferenceLoaded,
138147
genericTypeParameters: GenericTypeParameterData<ParameterRangeUnresolved>[],
139-
constructorCommentData: ConstructorCommentData,
140148
genericName: string,
141149
genericType: TypeNode | undefined,
142150
): void {

lib/parse/ParameterResolver.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { ClassIndex, ClassReference, ClassReferenceLoaded, InterfaceLoaded
55
import type { ClassLoader } from './ClassLoader';
66
import type { CommentLoader } from './CommentLoader';
77
import type { ConstructorData } from './ConstructorLoader';
8+
import type { GenericsData } from './GenericsLoader';
89
import type {
910
ExtensionData,
1011
GenericTypeParameterData,
@@ -56,11 +57,6 @@ export class ParameterResolver {
5657
unresolvedConstructorData: ConstructorData<ParameterRangeUnresolved>,
5758
): Promise<ConstructorData<ParameterRangeResolved>> {
5859
return {
59-
genericTypeParameters: await this.resolveGenericTypeParameterData(
60-
unresolvedConstructorData.genericTypeParameters,
61-
unresolvedConstructorData.classLoaded,
62-
{},
63-
),
6460
parameters: <ParameterDataField<ParameterRangeResolved>[]> (await this.resolveParameterData(
6561
unresolvedConstructorData.parameters,
6662
unresolvedConstructorData.classLoaded,
@@ -70,6 +66,31 @@ export class ParameterResolver {
7066
};
7167
}
7268

69+
/**
70+
* Resolve all generic type parameters of a given constructor index.
71+
* @param unresolvedParametersIndex An index of unresolved constructor data.
72+
*/
73+
public async resolveAllGenericTypeParameterData(
74+
unresolvedParametersIndex: ClassIndex<GenericsData<ParameterRangeUnresolved>>,
75+
): Promise<ClassIndex<GenericsData<ParameterRangeResolved>>> {
76+
const resolvedGenericsIndex: ClassIndex<GenericsData<ParameterRangeResolved>> = {};
77+
78+
// Resolve parameters for the different constructors in parallel
79+
await Promise.all(Object.entries(unresolvedParametersIndex)
80+
.map(async([ className, unresolvedGenericsData ]) => {
81+
resolvedGenericsIndex[className] = {
82+
genericTypeParameters: await this.resolveGenericTypeParameterData(
83+
unresolvedGenericsData.genericTypeParameters,
84+
unresolvedGenericsData.classLoaded,
85+
{},
86+
),
87+
classLoaded: unresolvedGenericsData.classLoaded,
88+
};
89+
}));
90+
91+
return resolvedGenericsIndex;
92+
}
93+
7394
/**
7495
* Resolve the given array of generic type parameter data in parallel.
7596
* @param genericTypeParameters An array of unresolved generic type parameters.

lib/serialize/ComponentConstructor.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
ClassReferenceLoadedClassOrInterface,
99
} from '../parse/ClassIndex';
1010
import type { ConstructorData } from '../parse/ConstructorLoader';
11+
import type { GenericsData } from '../parse/GenericsLoader';
1112
import type { PackageMetadata } from '../parse/PackageMetadataLoader';
1213
import type { DefaultNested,
1314
DefaultValue,
@@ -33,6 +34,7 @@ export class ComponentConstructor {
3334
private readonly pathDestination: PathDestinationDefinition;
3435
private readonly classAndInterfaceIndex: ClassIndex<ClassReferenceLoadedClassOrInterface>;
3536
private readonly classConstructors: ClassIndex<ConstructorData<ParameterRangeResolved>>;
37+
private readonly classGenerics: ClassIndex<GenericsData<ParameterRangeResolved>>;
3638
private readonly classExtensions: ClassIndex<ExtensionData<ParameterRangeResolved>[]>;
3739
private readonly externalComponents: ExternalComponents;
3840
private readonly contextParser: ContextParser;
@@ -44,6 +46,7 @@ export class ComponentConstructor {
4446
this.pathDestination = args.pathDestination;
4547
this.classAndInterfaceIndex = args.classAndInterfaceIndex;
4648
this.classConstructors = args.classConstructors;
49+
this.classGenerics = args.classGenerics;
4750
this.classExtensions = args.classExtensions;
4851
this.externalComponents = args.externalComponents;
4952
this.contextParser = args.contextParser;
@@ -84,6 +87,7 @@ export class ComponentConstructor {
8487
},
8588
classReference,
8689
this.classConstructors[className],
90+
this.classGenerics[className],
8791
this.classExtensions[className],
8892
));
8993
}
@@ -166,22 +170,24 @@ export class ComponentConstructor {
166170
* @param externalContextsCallback Callback for external contexts.
167171
* @param classReference Class reference of the class component.
168172
* @param constructorData Constructor data of the owning class.
173+
* @param genericsData Generics data of the owning class.
169174
* @param classExtensions Class extensions of the owning class.
170175
*/
171176
public async constructComponent(
172177
context: JsonLdContextNormalized,
173178
externalContextsCallback: ExternalContextCallback,
174179
classReference: ClassReferenceLoadedClassOrInterface,
175180
constructorData: ConstructorData<ParameterRangeResolved> | undefined,
181+
genericsData: GenericsData<ParameterRangeResolved> | undefined,
176182
classExtensions: ExtensionData<ParameterRangeResolved>[] | undefined,
177183
): Promise<ComponentDefinition> {
178184
// Determine generic type parameters
179-
const genericTypeParameters = constructorData ?
185+
const genericTypeParameters = genericsData ?
180186
await this.constructGenericTypeParameters(
181187
context,
182188
externalContextsCallback,
183189
classReference,
184-
constructorData.genericTypeParameters,
190+
genericsData.genericTypeParameters,
185191
) :
186192
undefined;
187193

@@ -799,6 +805,7 @@ export interface ComponentConstructorArgs {
799805
pathDestination: PathDestinationDefinition;
800806
classAndInterfaceIndex: ClassIndex<ClassReferenceLoadedClassOrInterface>;
801807
classConstructors: ClassIndex<ConstructorData<ParameterRangeResolved>>;
808+
classGenerics: ClassIndex<GenericsData<ParameterRangeResolved>>;
802809
classExtensions: ClassIndex<ExtensionData<ParameterRangeResolved>[]>;
803810
externalComponents: ExternalComponents;
804811
contextParser: ContextParser;

test/parse/ConstructorLoader.test.ts

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ describe('ConstructorLoader', () => {
4343
A,
4444
})).toEqual({
4545
A: {
46-
genericTypeParameters: [],
4746
parameters: [],
4847
classLoaded: A,
4948
},
@@ -64,7 +63,6 @@ describe('ConstructorLoader', () => {
6463
A,
6564
})).toEqual({
6665
A: {
67-
genericTypeParameters: [],
6866
parameters: [],
6967
classLoaded: A,
7068
},
@@ -109,7 +107,6 @@ export class B{
109107
B,
110108
})).toEqual({
111109
A: {
112-
genericTypeParameters: [],
113110
parameters: [
114111
{
115112
type: 'field',
@@ -144,7 +141,6 @@ export class B{
144141
classLoaded: A,
145142
},
146143
B: {
147-
genericTypeParameters: [],
148144
parameters: [
149145
{
150146
type: 'field',
@@ -191,7 +187,6 @@ export class B{
191187
A,
192188
})).toEqual({
193189
A: {
194-
genericTypeParameters: [],
195190
parameters: [
196191
{
197192
type: 'field',
@@ -226,58 +221,6 @@ export class B{
226221
},
227222
});
228223
});
229-
230-
it('should return for a single class with generic types', async() => {
231-
resolutionContext.contentsOverrides = {
232-
'file.d.ts': `export class A<A, B extends number, C extends Class<B, string>>{
233-
constructor(){}
234-
}`,
235-
};
236-
const A = await classIndexer.loadClassChain({
237-
packageName: 'p',
238-
localName: 'A',
239-
fileName: 'file',
240-
fileNameReferenced: 'fileReferenced',
241-
});
242-
expect(parser.getConstructors({
243-
A,
244-
})).toEqual({
245-
A: {
246-
genericTypeParameters: [
247-
{
248-
name: 'A',
249-
},
250-
{
251-
name: 'B',
252-
range: {
253-
type: 'raw',
254-
value: 'number',
255-
},
256-
},
257-
{
258-
name: 'C',
259-
range: {
260-
type: 'interface',
261-
value: 'Class',
262-
genericTypeParameterInstantiations: [
263-
{
264-
type: 'genericTypeReference',
265-
value: 'B',
266-
},
267-
{
268-
type: 'raw',
269-
value: 'string',
270-
},
271-
],
272-
origin: expect.anything(),
273-
},
274-
},
275-
],
276-
parameters: [],
277-
classLoaded: A,
278-
},
279-
});
280-
});
281224
});
282225

283226
describe('getConstructor', () => {

0 commit comments

Comments
 (0)