Skip to content

Commit 45e6805

Browse files
authored
Merge pull request #38688 from microsoft/fix-implements-tag-emit
Fix @implements emit for namespaced base types
2 parents 1704b2f + b1dfb68 commit 45e6805

6 files changed

+176
-15
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"node": ">=4.2.0"
3030
},
3131
"devDependencies": {
32-
"@octokit/rest": "latest",
32+
"@octokit/rest": "17.11.2",
3333
"@types/browserify": "latest",
3434
"@types/chai": "latest",
3535
"@types/convert-source-map": "latest",

src/compiler/checker.ts

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3856,16 +3856,21 @@ namespace ts {
38563856
}
38573857

38583858
function isTypeSymbolAccessible(typeSymbol: Symbol, enclosingDeclaration: Node | undefined): boolean {
3859-
const access = isSymbolAccessible(typeSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false);
3859+
const access = isSymbolAccessibleWorker(typeSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false, /*allowModules*/ true);
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 isSymbolAccessibleByFlags(typeSymbol: Symbol, enclosingDeclaration: Node | undefined, flags: SymbolFlags): boolean {
3869+
const access = isSymbolAccessibleWorker(typeSymbol, enclosingDeclaration, flags, /*shouldComputeAliasesToMakeVisible*/ false, /*allowModules*/ false);
3870+
return access.accessibility === SymbolAccessibility.Accessible;
3871+
}
3872+
3873+
function isAnySymbolAccessible(symbols: Symbol[] | undefined, enclosingDeclaration: Node | undefined, initialSymbol: Symbol, meaning: SymbolFlags, shouldComputeAliasesToMakeVisible: boolean, allowModules: boolean): SymbolAccessibilityResult | undefined {
38693874
if (!length(symbols)) return;
38703875

38713876
let hadAccessibleChain: Symbol | undefined;
@@ -3880,7 +3885,7 @@ namespace ts {
38803885
return hasAccessibleDeclarations;
38813886
}
38823887
}
3883-
else {
3888+
else if (allowModules) {
38843889
if (some(symbol.declarations, hasNonGlobalAugmentationExternalModuleSymbol)) {
38853890
if (shouldComputeAliasesToMakeVisible) {
38863891
earlyModuleBail = true;
@@ -3920,7 +3925,7 @@ namespace ts {
39203925
containers = [getSymbolOfNode(firstDecl.parent)];
39213926
}
39223927
}
3923-
const parentResult = isAnySymbolAccessible(containers, enclosingDeclaration, initialSymbol, initialSymbol === symbol ? getQualifiedLeftMeaning(meaning) : meaning, shouldComputeAliasesToMakeVisible);
3928+
const parentResult = isAnySymbolAccessible(containers, enclosingDeclaration, initialSymbol, initialSymbol === symbol ? getQualifiedLeftMeaning(meaning) : meaning, shouldComputeAliasesToMakeVisible, allowModules);
39243929
if (parentResult) {
39253930
return parentResult;
39263931
}
@@ -3950,8 +3955,12 @@ namespace ts {
39503955
* @param shouldComputeAliasToMakeVisible a boolean value to indicate whether to return aliases to be mark visible in case the symbol is accessible
39513956
*/
39523957
function isSymbolAccessible(symbol: Symbol | undefined, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, shouldComputeAliasesToMakeVisible: boolean): SymbolAccessibilityResult {
3958+
return isSymbolAccessibleWorker(symbol, enclosingDeclaration, meaning, shouldComputeAliasesToMakeVisible, /*allowModules*/ true);
3959+
}
3960+
3961+
function isSymbolAccessibleWorker(symbol: Symbol | undefined, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, shouldComputeAliasesToMakeVisible: boolean, allowModules: boolean): SymbolAccessibilityResult {
39533962
if (symbol && enclosingDeclaration) {
3954-
const result = isAnySymbolAccessible([symbol], enclosingDeclaration, symbol, meaning, shouldComputeAliasesToMakeVisible);
3963+
const result = isAnySymbolAccessible([symbol], enclosingDeclaration, symbol, meaning, shouldComputeAliasesToMakeVisible, allowModules);
39553964
if (result) {
39563965
return result;
39573966
}
@@ -6211,7 +6220,7 @@ namespace ts {
62116220
const constructSignatures = serializeSignatures(SignatureKind.Construct, interfaceType, baseType, SyntaxKind.ConstructSignature) as ConstructSignatureDeclaration[];
62126221
const indexSignatures = serializeIndexSignatures(interfaceType, baseType);
62136222

6214-
const heritageClauses = !length(baseTypes) ? undefined : [createHeritageClause(SyntaxKind.ExtendsKeyword, mapDefined(baseTypes, b => trySerializeAsTypeReference(b)))];
6223+
const heritageClauses = !length(baseTypes) ? undefined : [createHeritageClause(SyntaxKind.ExtendsKeyword, mapDefined(baseTypes, b => trySerializeAsTypeReference(b, SymbolFlags.Value)))];
62156224
addResult(createInterfaceDeclaration(
62166225
/*decorators*/ undefined,
62176226
/*modifiers*/ undefined,
@@ -6371,15 +6380,15 @@ namespace ts {
63716380
const typeParamDecls = map(localParams, p => typeParameterToDeclaration(p, context));
63726381
const classType = getDeclaredTypeOfClassOrInterface(symbol);
63736382
const baseTypes = getBaseTypes(classType);
6374-
const implementsTypes = getImplementsTypes(classType);
6383+
const implementsExpressions = mapDefined(getImplementsTypes(classType), serializeImplementedType);
63756384
const staticType = getTypeOfSymbol(symbol);
63766385
const isClass = !!staticType.symbol?.valueDeclaration && isClassLike(staticType.symbol.valueDeclaration);
63776386
const staticBaseType = isClass
63786387
? getBaseConstructorTypeOfClass(staticType as InterfaceType)
63796388
: anyType;
63806389
const heritageClauses = [
63816390
...!length(baseTypes) ? [] : [createHeritageClause(SyntaxKind.ExtendsKeyword, map(baseTypes, b => serializeBaseType(b, staticBaseType, localName)))],
6382-
...!length(implementsTypes) ? [] : [createHeritageClause(SyntaxKind.ImplementsKeyword, map(implementsTypes, b => serializeBaseType(b, staticBaseType, localName)))]
6391+
...!length(implementsExpressions) ? [] : [createHeritageClause(SyntaxKind.ImplementsKeyword, implementsExpressions)]
63836392
];
63846393
const symbolProps = getNonInterhitedProperties(classType, baseTypes, getPropertiesOfType(classType));
63856394
const publicSymbolProps = filter(symbolProps, s => {
@@ -6870,7 +6879,7 @@ namespace ts {
68706879
}
68716880

68726881
function serializeBaseType(t: Type, staticType: Type, rootName: string) {
6873-
const ref = trySerializeAsTypeReference(t);
6882+
const ref = trySerializeAsTypeReference(t, SymbolFlags.Value);
68746883
if (ref) {
68756884
return ref;
68766885
}
@@ -6882,23 +6891,34 @@ namespace ts {
68826891
return createExpressionWithTypeArguments(/*typeArgs*/ undefined, createIdentifier(tempName));
68836892
}
68846893

6885-
function trySerializeAsTypeReference(t: Type) {
6894+
function trySerializeAsTypeReference(t: Type, flags: SymbolFlags) {
68866895
let typeArgs: TypeNode[] | undefined;
68876896
let reference: Expression | undefined;
6897+
68886898
// We don't use `isValueSymbolAccessible` below. since that considers alternative containers (like modules)
68896899
// which we can't write out in a syntactically valid way as an expression
6890-
if ((t as TypeReference).target && getAccessibleSymbolChain((t as TypeReference).target.symbol, enclosingDeclaration, SymbolFlags.Value, /*useOnlyExternalAliasing*/ false)) {
6900+
if ((t as TypeReference).target && isSymbolAccessibleByFlags((t as TypeReference).target.symbol, enclosingDeclaration, flags)) {
68916901
typeArgs = map(getTypeArguments(t as TypeReference), t => typeToTypeNodeHelper(t, context));
68926902
reference = symbolToExpression((t as TypeReference).target.symbol, context, SymbolFlags.Type);
68936903
}
6894-
else if (t.symbol && getAccessibleSymbolChain(t.symbol, enclosingDeclaration, SymbolFlags.Value, /*useOnlyExternalAliasing*/ false)) {
6904+
else if (t.symbol && isSymbolAccessibleByFlags(t.symbol, enclosingDeclaration, flags)) {
68956905
reference = symbolToExpression(t.symbol, context, SymbolFlags.Type);
68966906
}
68976907
if (reference) {
68986908
return createExpressionWithTypeArguments(typeArgs, reference);
68996909
}
69006910
}
69016911

6912+
function serializeImplementedType(t: Type) {
6913+
const ref = trySerializeAsTypeReference(t, SymbolFlags.Type);
6914+
if (ref) {
6915+
return ref;
6916+
}
6917+
if (t.symbol) {
6918+
return createExpressionWithTypeArguments(/*typeArgs*/ undefined, symbolToExpression(t.symbol, context, SymbolFlags.Type));
6919+
}
6920+
}
6921+
69026922
function getUnusedName(input: string, symbol?: Symbol): string {
69036923
if (symbol) {
69046924
if (context.remappedSymbolNames!.has("" + getSymbolId(symbol))) {
@@ -24063,7 +24083,7 @@ namespace ts {
2406324083
// if jsx emit was not react as there wont be error being emitted
2406424084
reactSym.isReferenced = SymbolFlags.All;
2406524085

24066-
// If react symbol is alias, mark it as refereced
24086+
// If react symbol is alias, mark it as referenced
2406724087
if (reactSym.flags & SymbolFlags.Alias && !getTypeOnlyAliasDeclaration(reactSym)) {
2406824088
markAliasSymbolAsReferenced(reactSym);
2406924089
}
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)