Skip to content

Commit fc920bd

Browse files
committed
fix: Postpone resolution of inherited classes until their parents have been resolved
Actually fixes #1580 this time!
1 parent 12236f3 commit fc920bd

File tree

2 files changed

+112
-80
lines changed

2 files changed

+112
-80
lines changed

src/lib/converter/plugins/ImplementsPlugin.ts

+108-78
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
SignatureReflection,
77
} from "../../models/reflections/index";
88
import { ReferenceType, Type } from "../../models/types/index";
9-
import { zip } from "../../utils/array";
9+
import { filterMap, zip } from "../../utils/array";
1010
import { Component, ConverterComponent } from "../components";
1111
import { Context } from "../context";
1212
import { Converter } from "../converter";
@@ -18,6 +18,9 @@ import { copyComment } from "../utils/reflections";
1818
*/
1919
@Component({ name: "implements" })
2020
export class ImplementsPlugin extends ConverterComponent {
21+
private resolved = new WeakSet<Reflection>();
22+
private postponed = new WeakMap<Reflection, Set<DeclarationReflection>>();
23+
2124
/**
2225
* Create a new ImplementsPlugin instance.
2326
*/
@@ -53,101 +56,96 @@ export class ImplementsPlugin extends ConverterComponent {
5356
return;
5457
}
5558

56-
interfaceReflection.children.forEach(
57-
(interfaceMember: DeclarationReflection) => {
58-
if (!(interfaceMember instanceof DeclarationReflection)) {
59-
return;
60-
}
59+
interfaceReflection.children.forEach((interfaceMember) => {
60+
let classMember: DeclarationReflection | undefined;
6161

62-
let classMember: DeclarationReflection | undefined;
62+
if (!classReflection.children) {
63+
return;
64+
}
6365

64-
if (!classReflection.children) {
65-
return;
66+
for (
67+
let index = 0, count = classReflection.children.length;
68+
index < count;
69+
index++
70+
) {
71+
const child = classReflection.children[index];
72+
if (child.name !== interfaceMember.name) {
73+
continue;
6674
}
67-
68-
for (
69-
let index = 0, count = classReflection.children.length;
70-
index < count;
71-
index++
72-
) {
73-
const child = classReflection.children[index];
74-
if (child.name !== interfaceMember.name) {
75-
continue;
76-
}
77-
if (
78-
child.flags.isStatic !== interfaceMember.flags.isStatic
79-
) {
80-
continue;
81-
}
82-
83-
classMember = child;
84-
break;
75+
if (child.flags.isStatic !== interfaceMember.flags.isStatic) {
76+
continue;
8577
}
8678

87-
if (!classMember) {
88-
return;
89-
}
79+
classMember = child;
80+
break;
81+
}
9082

91-
const interfaceMemberName =
92-
interfaceReflection.name + "." + interfaceMember.name;
93-
classMember.implementationOf = new ReferenceType(
94-
interfaceMemberName,
95-
interfaceMember,
96-
context.project
97-
);
98-
copyComment(classMember, interfaceMember);
83+
if (!classMember) {
84+
return;
85+
}
9986

100-
if (
101-
interfaceMember.kindOf(ReflectionKind.Property) &&
102-
classMember.kindOf(ReflectionKind.Accessor)
103-
) {
104-
if (classMember.getSignature) {
105-
copyComment(classMember.getSignature, interfaceMember);
106-
classMember.getSignature.implementationOf =
107-
classMember.implementationOf;
108-
}
109-
if (classMember.setSignature) {
110-
copyComment(classMember.setSignature, interfaceMember);
111-
classMember.setSignature.implementationOf =
112-
classMember.implementationOf;
113-
}
87+
const interfaceMemberName =
88+
interfaceReflection.name + "." + interfaceMember.name;
89+
classMember.implementationOf = new ReferenceType(
90+
interfaceMemberName,
91+
interfaceMember,
92+
context.project
93+
);
94+
copyComment(classMember, interfaceMember);
95+
96+
if (
97+
interfaceMember.kindOf(ReflectionKind.Property) &&
98+
classMember.kindOf(ReflectionKind.Accessor)
99+
) {
100+
if (classMember.getSignature) {
101+
copyComment(classMember.getSignature, interfaceMember);
102+
classMember.getSignature.implementationOf =
103+
classMember.implementationOf;
104+
}
105+
if (classMember.setSignature) {
106+
copyComment(classMember.setSignature, interfaceMember);
107+
classMember.setSignature.implementationOf =
108+
classMember.implementationOf;
114109
}
110+
}
115111

116-
if (
117-
interfaceMember.kindOf(ReflectionKind.FunctionOrMethod) &&
118-
interfaceMember.signatures &&
119-
classMember.signatures
120-
) {
121-
for (const [clsSig, intSig] of zip(
122-
classMember.signatures,
123-
interfaceMember.signatures
124-
)) {
125-
if (clsSig.implementationOf) {
126-
clsSig.implementationOf = new ReferenceType(
127-
clsSig.implementationOf.name,
128-
intSig,
129-
context.project
130-
);
131-
}
132-
copyComment(clsSig, intSig);
112+
if (
113+
interfaceMember.kindOf(ReflectionKind.FunctionOrMethod) &&
114+
interfaceMember.signatures &&
115+
classMember.signatures
116+
) {
117+
for (const [clsSig, intSig] of zip(
118+
classMember.signatures,
119+
interfaceMember.signatures
120+
)) {
121+
if (clsSig.implementationOf) {
122+
clsSig.implementationOf = new ReferenceType(
123+
clsSig.implementationOf.name,
124+
intSig,
125+
context.project
126+
);
133127
}
128+
copyComment(clsSig, intSig);
134129
}
135130
}
136-
);
131+
});
137132
}
138133

139134
private analyzeInheritance(
140135
context: Context,
141136
reflection: DeclarationReflection
142137
) {
143-
const extendedTypes = (reflection.extendedTypes?.filter((type) => {
144-
return (
145-
type instanceof ReferenceType &&
146-
type.reflection instanceof DeclarationReflection
147-
);
148-
}) ?? []) as Array<
149-
ReferenceType & { reflection: DeclarationReflection }
150-
>;
138+
const extendedTypes = filterMap(
139+
reflection.extendedTypes ?? [],
140+
(type) => {
141+
return type instanceof ReferenceType &&
142+
type.reflection instanceof DeclarationReflection
143+
? (type as ReferenceType & {
144+
reflection: DeclarationReflection;
145+
})
146+
: void 0;
147+
}
148+
);
151149

152150
for (const parent of extendedTypes) {
153151
for (const parentMember of parent.reflection.children ?? []) {
@@ -192,6 +190,38 @@ export class ImplementsPlugin extends ConverterComponent {
192190
* @param reflection The reflection that is currently resolved.
193191
*/
194192
private onResolve(context: Context, reflection: DeclarationReflection) {
193+
this.tryResolve(context, reflection);
194+
}
195+
196+
private tryResolve(context: Context, reflection: DeclarationReflection) {
197+
const requirements = filterMap(
198+
[
199+
...(reflection.implementedTypes ?? []),
200+
...(reflection.extendedTypes ?? []),
201+
],
202+
(type) => {
203+
return type instanceof ReferenceType ? type.reflection : void 0;
204+
}
205+
);
206+
207+
if (requirements.every((req) => this.resolved.has(req))) {
208+
this.doResolve(context, reflection);
209+
this.resolved.add(reflection);
210+
211+
for (const refl of this.postponed.get(reflection) ?? []) {
212+
this.tryResolve(context, refl);
213+
}
214+
this.postponed.delete(reflection);
215+
} else {
216+
for (const req of requirements) {
217+
const future = this.postponed.get(req) ?? new Set();
218+
future.add(reflection);
219+
this.postponed.set(req, future);
220+
}
221+
}
222+
}
223+
224+
private doResolve(context: Context, reflection: DeclarationReflection) {
195225
if (
196226
reflection.kindOf(ReflectionKind.Class) &&
197227
reflection.implementedTypes

src/test/converter2/issues/gh1580.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
export class A {
1+
export { B, A };
2+
3+
class A {
24
/** Prop docs */
35
prop!: string;
46

@@ -8,7 +10,7 @@ export class A {
810
}
911
}
1012

11-
export class B extends A {
13+
class B extends A {
1214
declare prop: "B";
1315

1416
run(): void {

0 commit comments

Comments
 (0)