Skip to content

Commit c5f6671

Browse files
committed
Fix @implements emit for namespaced base types
Fixes #38640
1 parent 7ec467e commit c5f6671

9 files changed

+192
-11
lines changed

src/compiler/checker.ts

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3855,17 +3855,17 @@ namespace ts {
38553855
return false;
38563856
}
38573857

3858-
function isTypeSymbolAccessible(typeSymbol: Symbol, enclosingDeclaration: Node | undefined): boolean {
3859-
const access = isSymbolAccessible(typeSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false);
3858+
function isTypeSymbolAccessible(typeSymbol: Symbol, enclosingDeclaration: Node | undefined, allowModules: boolean): boolean {
3859+
const access = isSymbolAccessibleWorker(typeSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false, allowModules);
38603860
return access.accessibility === SymbolAccessibility.Accessible;
38613861
}
38623862

38633863
function isValueSymbolAccessible(typeSymbol: Symbol, enclosingDeclaration: Node | undefined): boolean {
3864-
const access = isSymbolAccessible(typeSymbol, enclosingDeclaration, SymbolFlags.Value, /*shouldComputeAliasesToMakeVisible*/ false);
3864+
const access = isSymbolAccessibleWorker(typeSymbol, enclosingDeclaration, SymbolFlags.Value, /*shouldComputeAliasesToMakeVisible*/ false, /*allowModules*/ true);
38653865
return access.accessibility === SymbolAccessibility.Accessible;
38663866
}
38673867

3868-
function isAnySymbolAccessible(symbols: Symbol[] | undefined, enclosingDeclaration: Node | undefined, initialSymbol: Symbol, meaning: SymbolFlags, shouldComputeAliasesToMakeVisible: boolean): SymbolAccessibilityResult | undefined {
3868+
function isAnySymbolAccessible(symbols: Symbol[] | undefined, enclosingDeclaration: Node | undefined, initialSymbol: Symbol, meaning: SymbolFlags, shouldComputeAliasesToMakeVisible: boolean, allowModules: boolean): SymbolAccessibilityResult | undefined {
38693869
if (!length(symbols)) return;
38703870

38713871
let hadAccessibleChain: Symbol | undefined;
@@ -3880,7 +3880,7 @@ namespace ts {
38803880
return hasAccessibleDeclarations;
38813881
}
38823882
}
3883-
else {
3883+
else if (allowModules) {
38843884
if (some(symbol.declarations, hasNonGlobalAugmentationExternalModuleSymbol)) {
38853885
if (shouldComputeAliasesToMakeVisible) {
38863886
earlyModuleBail = true;
@@ -3920,7 +3920,7 @@ namespace ts {
39203920
containers = [getSymbolOfNode(firstDecl.parent)];
39213921
}
39223922
}
3923-
const parentResult = isAnySymbolAccessible(containers, enclosingDeclaration, initialSymbol, initialSymbol === symbol ? getQualifiedLeftMeaning(meaning) : meaning, shouldComputeAliasesToMakeVisible);
3923+
const parentResult = isAnySymbolAccessible(containers, enclosingDeclaration, initialSymbol, initialSymbol === symbol ? getQualifiedLeftMeaning(meaning) : meaning, shouldComputeAliasesToMakeVisible, allowModules);
39243924
if (parentResult) {
39253925
return parentResult;
39263926
}
@@ -3950,8 +3950,12 @@ namespace ts {
39503950
* @param shouldComputeAliasToMakeVisible a boolean value to indicate whether to return aliases to be mark visible in case the symbol is accessible
39513951
*/
39523952
function isSymbolAccessible(symbol: Symbol | undefined, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, shouldComputeAliasesToMakeVisible: boolean): SymbolAccessibilityResult {
3953+
return isSymbolAccessibleWorker(symbol, enclosingDeclaration, meaning, shouldComputeAliasesToMakeVisible, true);
3954+
}
3955+
3956+
function isSymbolAccessibleWorker(symbol: Symbol | undefined, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, shouldComputeAliasesToMakeVisible: boolean, allowModules: boolean): SymbolAccessibilityResult {
39533957
if (symbol && enclosingDeclaration) {
3954-
const result = isAnySymbolAccessible([symbol], enclosingDeclaration, symbol, meaning, shouldComputeAliasesToMakeVisible);
3958+
const result = isAnySymbolAccessible([symbol], enclosingDeclaration, symbol, meaning, shouldComputeAliasesToMakeVisible, allowModules);
39553959
if (result) {
39563960
return result;
39573961
}
@@ -4331,7 +4335,7 @@ namespace ts {
43314335
return createThis();
43324336
}
43334337

4334-
if (!inTypeAlias && type.aliasSymbol && (context.flags & NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope || isTypeSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration))) {
4338+
if (!inTypeAlias && type.aliasSymbol && (context.flags & NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope || isTypeSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration, /*allowModules*/ true))) {
43354339
const typeArgumentNodes = mapToTypeNodes(type.aliasTypeArguments, context);
43364340
if (isReservedMemberName(type.aliasSymbol.escapedName) && !(type.aliasSymbol.flags & SymbolFlags.Class)) return createTypeReferenceNode(createIdentifier(""), typeArgumentNodes);
43374341
return symbolToTypeNode(type.aliasSymbol, context, SymbolFlags.Type, typeArgumentNodes);
@@ -4350,7 +4354,7 @@ namespace ts {
43504354
}
43514355
if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams &&
43524356
type.flags & TypeFlags.TypeParameter &&
4353-
!isTypeSymbolAccessible(type.symbol, context.enclosingDeclaration)) {
4357+
!isTypeSymbolAccessible(type.symbol, context.enclosingDeclaration, /*allowModules*/ true)) {
43544358
const name = typeParameterToName(type, context);
43554359
context.approximateLength += idText(name).length;
43564360
return createTypeReferenceNode(createIdentifier(idText(name)), /*typeArguments*/ undefined);
@@ -6325,7 +6329,7 @@ namespace ts {
63256329
: anyType;
63266330
const heritageClauses = [
63276331
...!length(baseTypes) ? [] : [createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))],
6328-
...!length(implementsTypes) ? [] : [createHeritageClause(SyntaxKind.ImplementsKeyword, map(implementsTypes, b => serializeBaseType(b, staticBaseType, localName)))]
6332+
...!length(implementsTypes) ? [] : [createHeritageClause(SyntaxKind.ImplementsKeyword, map(implementsTypes, b => serializeImplementedType(b)))]
63296333
];
63306334
const symbolProps = getNonInterhitedProperties(classType, baseTypes, getPropertiesOfType(classType));
63316335
const publicSymbolProps = filter(symbolProps, s => {
@@ -6838,6 +6842,28 @@ namespace ts {
68386842
}
68396843
}
68406844

6845+
function serializeImplementedType(t: Type) {
6846+
let typeArgs: TypeNode[] | undefined;
6847+
let reference: Expression | undefined;
6848+
if ((t as TypeReference).target && isTypeSymbolAccessible((t as TypeReference).target.symbol, enclosingDeclaration, /*allowModules*/ false)) {
6849+
typeArgs = map(getTypeArguments(t as TypeReference), t => typeToTypeNodeHelper(t, context));
6850+
reference = symbolToExpression((t as TypeReference).target.symbol, context, SymbolFlags.Type);
6851+
}
6852+
else if (t.symbol && isTypeSymbolAccessible(t.symbol, enclosingDeclaration, /*allowModules*/ false)) {
6853+
reference = symbolToExpression(t.symbol, context, SymbolFlags.Type);
6854+
}
6855+
if (reference) {
6856+
return createExpressionWithTypeArguments(typeArgs, reference);
6857+
}
6858+
else if (context.tracker.reportInaccessibleBaseType) {
6859+
context.tracker.reportInaccessibleBaseType();
6860+
return createExpressionWithTypeArguments(/*typeArgs*/ undefined, symbolToExpression(t.symbol, context, SymbolFlags.Type));
6861+
}
6862+
else {
6863+
Debug.fail("context.tracker missing some error reporting methods");
6864+
}
6865+
}
6866+
68416867
function getUnusedName(input: string, symbol?: Symbol): string {
68426868
if (symbol) {
68436869
if (context.remappedSymbolNames!.has("" + getSymbolId(symbol))) {
@@ -23925,7 +23951,7 @@ namespace ts {
2392523951
// if jsx emit was not react as there wont be error being emitted
2392623952
reactSym.isReferenced = SymbolFlags.All;
2392723953

23928-
// If react symbol is alias, mark it as refereced
23954+
// If react symbol is alias, mark it as referenced
2392923955
if (reactSym.flags & SymbolFlags.Alias && !getTypeOnlyAliasDeclaration(reactSym)) {
2393023956
markAliasSymbolAsReferenced(reactSym);
2393123957
}

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2971,6 +2971,10 @@
29712971
"category": "Error",
29722972
"code": 2791
29732973
},
2974+
"The type '{0}' references an inaccessible base type.": {
2975+
"category": "Error",
2976+
"code": 2792
2977+
},
29742978

29752979
"Import declaration '{0}' is using private name '{1}'.": {
29762980
"category": "Error",

src/compiler/transformers/declarations.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ namespace ts {
7070
const host = context.getEmitHost();
7171
const symbolTracker: SymbolTracker = {
7272
trackSymbol,
73+
reportInaccessibleBaseType,
7374
reportInaccessibleThisError,
7475
reportInaccessibleUniqueSymbolError,
7576
reportPrivateInBaseOfClassExpression,
@@ -175,6 +176,13 @@ namespace ts {
175176
}
176177
}
177178

179+
function reportInaccessibleBaseType() {
180+
if (errorNameNode) {
181+
context.addDiagnostic(createDiagnosticForNode(errorNameNode, Diagnostics.The_type_0_references_an_inaccessible_base_type,
182+
declarationNameToString(errorNameNode)));
183+
}
184+
}
185+
178186
function reportInaccessibleThisError() {
179187
if (errorNameNode) {
180188
context.addDiagnostic(createDiagnosticForNode(errorNameNode, Diagnostics.The_inferred_type_of_0_references_an_inaccessible_1_type_A_type_annotation_is_necessary,

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6465,6 +6465,7 @@ namespace ts {
64656465
// declaration emitter to help determine if it should patch up the final declaration file
64666466
// with import statements it previously saw (but chose not to emit).
64676467
trackSymbol?(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags): void;
6468+
reportInaccessibleBaseType?(): void;
64686469
reportInaccessibleThisError?(): void;
64696470
reportPrivateInBaseOfClassExpression?(propertyName: string): void;
64706471
reportInaccessibleUniqueSymbolError?(): void;

src/compiler/utilities.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ namespace ts {
8080
decreaseIndent: noop,
8181
clear: () => str = "",
8282
trackSymbol: noop,
83+
reportInaccessibleBaseType: noop,
8384
reportInaccessibleThisError: noop,
8485
reportInaccessibleUniqueSymbolError: noop,
8586
reportPrivateInBaseOfClassExpression: noop,
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//// [tests/cases/conformance/jsdoc/jsdocImplements_namespacedInterface.ts] ////
2+
3+
//// [defs.d.ts]
4+
declare namespace N {
5+
interface A {
6+
mNumber(): number;
7+
}
8+
interface AT<T> {
9+
gen(): T;
10+
}
11+
}
12+
//// [a.js]
13+
/** @implements N.A */
14+
class B {
15+
mNumber() {
16+
return 0;
17+
}
18+
}
19+
/** @implements {N.AT<string>} */
20+
class BAT {
21+
gen() {
22+
return "";
23+
}
24+
}
25+
26+
27+
28+
29+
//// [a.d.ts]
30+
/** @implements N.A */
31+
declare class B implements N.A {
32+
mNumber(): number;
33+
}
34+
/** @implements {N.AT<string>} */
35+
declare class BAT implements N.AT<string> {
36+
gen(): string;
37+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
=== /defs.d.ts ===
2+
declare namespace N {
3+
>N : Symbol(N, Decl(defs.d.ts, 0, 0))
4+
5+
interface A {
6+
>A : Symbol(A, Decl(defs.d.ts, 0, 21))
7+
8+
mNumber(): number;
9+
>mNumber : Symbol(A.mNumber, Decl(defs.d.ts, 1, 17))
10+
}
11+
interface AT<T> {
12+
>AT : Symbol(AT, Decl(defs.d.ts, 3, 5))
13+
>T : Symbol(T, Decl(defs.d.ts, 4, 17))
14+
15+
gen(): T;
16+
>gen : Symbol(AT.gen, Decl(defs.d.ts, 4, 21))
17+
>T : Symbol(T, Decl(defs.d.ts, 4, 17))
18+
}
19+
}
20+
=== /a.js ===
21+
/** @implements N.A */
22+
class B {
23+
>B : Symbol(B, Decl(a.js, 0, 0))
24+
25+
mNumber() {
26+
>mNumber : Symbol(B.mNumber, Decl(a.js, 1, 9))
27+
28+
return 0;
29+
}
30+
}
31+
/** @implements {N.AT<string>} */
32+
class BAT {
33+
>BAT : Symbol(BAT, Decl(a.js, 5, 1))
34+
35+
gen() {
36+
>gen : Symbol(BAT.gen, Decl(a.js, 7, 11))
37+
38+
return "";
39+
}
40+
}
41+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
=== /defs.d.ts ===
2+
declare namespace N {
3+
interface A {
4+
mNumber(): number;
5+
>mNumber : () => number
6+
}
7+
interface AT<T> {
8+
gen(): T;
9+
>gen : () => T
10+
}
11+
}
12+
=== /a.js ===
13+
/** @implements N.A */
14+
class B {
15+
>B : B
16+
17+
mNumber() {
18+
>mNumber : () => number
19+
20+
return 0;
21+
>0 : 0
22+
}
23+
}
24+
/** @implements {N.AT<string>} */
25+
class BAT {
26+
>BAT : BAT
27+
28+
gen() {
29+
>gen : () => string
30+
31+
return "";
32+
>"" : ""
33+
}
34+
}
35+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// @allowJs: true
2+
// @checkJs: true
3+
// @declaration: true
4+
// @emitDeclarationOnly: true
5+
// @outDir: ./out
6+
7+
// @Filename: /defs.d.ts
8+
declare namespace N {
9+
interface A {
10+
mNumber(): number;
11+
}
12+
interface AT<T> {
13+
gen(): T;
14+
}
15+
}
16+
// @Filename: /a.js
17+
/** @implements N.A */
18+
class B {
19+
mNumber() {
20+
return 0;
21+
}
22+
}
23+
/** @implements {N.AT<string>} */
24+
class BAT {
25+
gen() {
26+
return "";
27+
}
28+
}

0 commit comments

Comments
 (0)