Skip to content

Commit 3fc727b

Browse files
authored
Use import types to refer to declarations in declaration emit (microsoft#24071)
* Stand up a simple implementation using import types for exports of modules which are otherwise inaccessible * Ensure references exist to link to modules containing utilized ambient modules * Accept baselines with new import type usage * Fix lint
1 parent 09b9ec4 commit 3fc727b

File tree

77 files changed

+1154
-1637
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+1154
-1637
lines changed

src/compiler/checker.ts

+93-60
Large diffs are not rendered by default.

src/compiler/transformers/declarations.ts

+12-4
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,23 @@ namespace ts {
3737
let lateStatementReplacementMap: Map<VisitResult<LateVisibilityPaintedStatement>>;
3838
let suppressNewDiagnosticContexts: boolean;
3939

40+
const host = context.getEmitHost();
4041
const symbolTracker: SymbolTracker = {
4142
trackSymbol,
4243
reportInaccessibleThisError,
4344
reportInaccessibleUniqueSymbolError,
44-
reportPrivateInBaseOfClassExpression
45+
reportPrivateInBaseOfClassExpression,
46+
moduleResolverHost: host,
47+
trackReferencedAmbientModule,
4548
};
4649
let errorNameNode: DeclarationName | undefined;
4750

4851
let currentSourceFile: SourceFile;
52+
let refs: Map<SourceFile>;
4953
const resolver = context.getEmitResolver();
5054
const options = context.getCompilerOptions();
5155
const newLine = getNewLineCharacter(options);
5256
const { noResolve, stripInternal } = options;
53-
const host = context.getEmitHost();
5457
return transformRoot;
5558

5659
function recordTypeReferenceDirectivesIfNecessary(typeReferenceDirectives: string[]): void {
@@ -63,6 +66,11 @@ namespace ts {
6366
}
6467
}
6568

69+
function trackReferencedAmbientModule(node: ModuleDeclaration) {
70+
const container = getSourceFileOfNode(node);
71+
refs.set("" + getOriginalNodeId(container), container);
72+
}
73+
6674
function handleSymbolAccessibilityError(symbolAccessibilityResult: SymbolAccessibilityResult) {
6775
if (symbolAccessibilityResult.accessibility === SymbolAccessibility.Accessible) {
6876
// Add aliases back onto the possible imports list if they're not there so we can try them again with updated visibility info
@@ -197,13 +205,13 @@ namespace ts {
197205
lateMarkedStatements = undefined;
198206
lateStatementReplacementMap = createMap();
199207
necessaryTypeRefernces = undefined;
200-
const refs = collectReferences(currentSourceFile, createMap());
208+
refs = collectReferences(currentSourceFile, createMap());
201209
const references: FileReference[] = [];
202210
const outputFilePath = getDirectoryPath(normalizeSlashes(getOutputPathsFor(node, host, /*forceDtsPaths*/ true).declarationFilePath));
203211
const referenceVisitor = mapReferencesIntoArray(references, outputFilePath);
204-
refs.forEach(referenceVisitor);
205212
const statements = visitNodes(node.statements, visitDeclarationStatements);
206213
let combinedStatements = setTextRange(createNodeArray(transformAndReplaceLatePaintedStatements(statements)), node.statements);
214+
refs.forEach(referenceVisitor);
207215
const emittedImports = filter(combinedStatements, isAnyImportSyntax);
208216
if (isExternalModule(node) && (!resultHasExternalModuleIndicator || (needsScopeFixMarker && !resultHasScopeMarker))) {
209217
combinedStatements = setTextRange(createNodeArray([...combinedStatements, createExportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createNamedExports([]), /*moduleSpecifier*/ undefined)]), combinedStatements);

src/compiler/types.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -3093,7 +3093,7 @@ namespace ts {
30933093
WriteArrayAsGenericType = 1 << 1, // Write Array<T> instead T[]
30943094
GenerateNamesForShadowedTypeParams = 1 << 2, // When a type parameter T is shadowing another T, generate a name for it so it can still be referenced
30953095
UseStructuralFallback = 1 << 3, // When an alias cannot be named by its symbol, rather than report an error, fallback to a structural printout if possible
3096-
// empty space
3096+
ForbidIndexedAccessSymbolReferences = 1 << 4, // Forbid references like `I["a"]["b"]` - print `typeof I.a<x>.b<y>` instead
30973097
WriteTypeArgumentsOfSignature = 1 << 5, // Write the type arguments instead of type parameters of the signature
30983098
UseFullyQualifiedType = 1 << 6, // Write out the fully qualified type name (eg. Module.Type, instead of Type)
30993099
UseOnlyExternalAliasing = 1 << 7, // Only use external aliases for a symbol
@@ -5258,6 +5258,13 @@ namespace ts {
52585258
isAtStartOfLine(): boolean;
52595259
}
52605260

5261+
/* @internal */
5262+
export interface ModuleNameResolverHost {
5263+
getCanonicalFileName(f: string): string;
5264+
getCommonSourceDirectory(): string;
5265+
getCurrentDirectory(): string;
5266+
}
5267+
52615268
/** @deprecated See comment on SymbolWriter */
52625269
// Note: this has non-deprecated internal uses.
52635270
export interface SymbolTracker {
@@ -5268,6 +5275,10 @@ namespace ts {
52685275
reportInaccessibleThisError?(): void;
52695276
reportPrivateInBaseOfClassExpression?(propertyName: string): void;
52705277
reportInaccessibleUniqueSymbolError?(): void;
5278+
/* @internal */
5279+
moduleResolverHost?: ModuleNameResolverHost;
5280+
/* @internal */
5281+
trackReferencedAmbientModule?(decl: ModuleDeclaration): void;
52715282
}
52725283

52735284
export interface TextSpan {

src/compiler/utilities.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -2878,11 +2878,11 @@ namespace ts {
28782878
};
28792879
}
28802880

2881-
export function getResolvedExternalModuleName(host: EmitHost, file: SourceFile): string {
2882-
return file.moduleName || getExternalModuleNameFromPath(host, file.fileName);
2881+
export function getResolvedExternalModuleName(host: ModuleNameResolverHost, file: SourceFile, referenceFile?: SourceFile): string {
2882+
return file.moduleName || getExternalModuleNameFromPath(host, file.fileName, referenceFile && referenceFile.fileName);
28832883
}
28842884

2885-
export function getExternalModuleNameFromDeclaration(host: EmitHost, resolver: EmitResolver, declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode): string {
2885+
export function getExternalModuleNameFromDeclaration(host: ModuleNameResolverHost, resolver: EmitResolver, declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode): string {
28862886
const file = resolver.getExternalModuleFileFromDeclaration(declaration);
28872887
if (!file || file.isDeclarationFile) {
28882888
return undefined;
@@ -2893,12 +2893,13 @@ namespace ts {
28932893
/**
28942894
* Resolves a local path to a path which is absolute to the base of the emit
28952895
*/
2896-
export function getExternalModuleNameFromPath(host: EmitHost, fileName: string): string {
2896+
export function getExternalModuleNameFromPath(host: ModuleNameResolverHost, fileName: string, referencePath?: string): string {
28972897
const getCanonicalFileName = (f: string) => host.getCanonicalFileName(f);
2898-
const dir = toPath(host.getCommonSourceDirectory(), host.getCurrentDirectory(), getCanonicalFileName);
2898+
const dir = toPath(referencePath ? getDirectoryPath(referencePath) : host.getCommonSourceDirectory(), host.getCurrentDirectory(), getCanonicalFileName);
28992899
const filePath = getNormalizedAbsolutePath(fileName, host.getCurrentDirectory());
29002900
const relativePath = getRelativePathToDirectoryOrUrl(dir, filePath, dir, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
2901-
return removeFileExtension(relativePath);
2901+
const extensionless = removeFileExtension(relativePath);
2902+
return referencePath ? ensurePathIsNonModuleName(extensionless) : extensionless;
29022903
}
29032904

29042905
export function getOwnEmitOutputFilePath(sourceFile: SourceFile, host: EmitHost, extension: string) {

tests/baselines/reference/amdDeclarationEmitNoExtraDeclare.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
=== tests/cases/compiler/Class.ts ===
22
import { Configurable } from "./Configurable"
3-
>Configurable : <T extends new (...args: any[]) => {}>(base: T) => T
3+
>Configurable : <T extends import("tests/cases/compiler/Configurable").Constructor<{}>>(base: T) => T
44

55
export class HiddenClass {}
66
>HiddenClass : HiddenClass
77

88
export class ActualClass extends Configurable(HiddenClass) {}
99
>ActualClass : ActualClass
1010
>Configurable(HiddenClass) : HiddenClass
11-
>Configurable : <T extends new (...args: any[]) => {}>(base: T) => T
11+
>Configurable : <T extends import("tests/cases/compiler/Configurable").Constructor<{}>>(base: T) => T
1212
>HiddenClass : typeof HiddenClass
1313

1414
=== tests/cases/compiler/Configurable.ts ===

tests/baselines/reference/api/tsserverlibrary.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1900,6 +1900,7 @@ declare namespace ts {
19001900
WriteArrayAsGenericType = 2,
19011901
GenerateNamesForShadowedTypeParams = 4,
19021902
UseStructuralFallback = 8,
1903+
ForbidIndexedAccessSymbolReferences = 16,
19031904
WriteTypeArgumentsOfSignature = 32,
19041905
UseFullyQualifiedType = 64,
19051906
UseOnlyExternalAliasing = 128,

tests/baselines/reference/api/typescript.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1900,6 +1900,7 @@ declare namespace ts {
19001900
WriteArrayAsGenericType = 2,
19011901
GenerateNamesForShadowedTypeParams = 4,
19021902
UseStructuralFallback = 8,
1903+
ForbidIndexedAccessSymbolReferences = 16,
19031904
WriteTypeArgumentsOfSignature = 32,
19041905
UseFullyQualifiedType = 64,
19051906
UseOnlyExternalAliasing = 128,

tests/baselines/reference/augmentExportEquals5.types

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ declare module "express" {
1818
function e(): e.Express;
1919
>e : typeof e
2020
>e : any
21-
>Express : Express
21+
>Express : e.Express
2222

2323
namespace e {
2424
>e : typeof e

tests/baselines/reference/declarationEmitAliasFromIndirectFile.js

+3-18
Original file line numberDiff line numberDiff line change
@@ -36,24 +36,9 @@ exports["default"] = fp.l10ns;
3636

3737
//// [app.d.ts]
3838
declare const _default: {
39-
ar?: {
40-
weekdays: {
41-
shorthand: [string, string, string, string, string, string, string];
42-
longhand: [string, string, string, string, string, string, string];
43-
};
44-
};
45-
bg?: {
46-
weekdays: {
47-
shorthand: [string, string, string, string, string, string, string];
48-
longhand: [string, string, string, string, string, string, string];
49-
};
50-
};
39+
ar?: import("./locale").CustomLocale;
40+
bg?: import("./locale").CustomLocale;
5141
} & {
52-
default: {
53-
weekdays: {
54-
shorthand: [string, string, string, string, string, string, string];
55-
longhand: [string, string, string, string, string, string, string];
56-
};
57-
};
42+
default: import("./locale").Locale;
5843
};
5944
export default _default;

tests/baselines/reference/declarationEmitAliasFromIndirectFile.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ const fp = { l10ns: {} } as FlatpickrFn;
6262
>FlatpickrFn : FlatpickrFn
6363

6464
export default fp.l10ns;
65-
>fp.l10ns : { ar?: { weekdays: { shorthand: [string, string, string, string, string, string, string]; longhand: [string, string, string, string, string, string, string]; }; }; bg?: { weekdays: { shorthand: [string, string, string, string, string, string, string]; longhand: [string, string, string, string, string, string, string]; }; }; } & { default: { weekdays: { shorthand: [string, string, string, string, string, string, string]; longhand: [string, string, string, string, string, string, string]; }; }; }
65+
>fp.l10ns : { ar?: import("tests/cases/compiler/locale").CustomLocale; bg?: import("tests/cases/compiler/locale").CustomLocale; } & { default: import("tests/cases/compiler/locale").Locale; }
6666
>fp : FlatpickrFn
67-
>l10ns : { ar?: { weekdays: { shorthand: [string, string, string, string, string, string, string]; longhand: [string, string, string, string, string, string, string]; }; }; bg?: { weekdays: { shorthand: [string, string, string, string, string, string, string]; longhand: [string, string, string, string, string, string, string]; }; }; } & { default: { weekdays: { shorthand: [string, string, string, string, string, string, string]; longhand: [string, string, string, string, string, string, string]; }; }; }
67+
>l10ns : { ar?: import("tests/cases/compiler/locale").CustomLocale; bg?: import("tests/cases/compiler/locale").CustomLocale; } & { default: import("tests/cases/compiler/locale").Locale; }
6868

tests/baselines/reference/declarationEmitInferredTypeAlias7.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,5 @@ exports.v = v;
2222
//// [0.d.ts]
2323
export declare type Data = string | boolean;
2424
//// [1.d.ts]
25-
declare let v: string | boolean;
25+
declare let v: import("./0").Data;
2626
export { v };

tests/baselines/reference/declarationEmitInferredTypeAlias7.types

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ let obj: Data = true;
99

1010
=== tests/cases/compiler/1.ts ===
1111
let v = "str" || true;
12-
>v : string | boolean
12+
>v : import("tests/cases/compiler/0").Data
1313
>"str" || true : true | "str"
1414
>"str" : "str"
1515
>true : true
1616

1717
export { v }
18-
>v : string | boolean
18+
>v : import("tests/cases/compiler/0").Data
1919

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//// [tests/cases/compiler/declarationsForInferredTypeFromOtherFile.ts] ////
2+
3+
//// [file1.ts]
4+
export class Foo {}
5+
//// [file2.ts]
6+
export function foo(): import("./file1").Foo {
7+
return null as any;
8+
}
9+
//// [file3.ts]
10+
import {foo} from "./file2";
11+
export function bar() {
12+
return foo();
13+
}
14+
15+
16+
//// [file1.js]
17+
"use strict";
18+
exports.__esModule = true;
19+
var Foo = /** @class */ (function () {
20+
function Foo() {
21+
}
22+
return Foo;
23+
}());
24+
exports.Foo = Foo;
25+
//// [file2.js]
26+
"use strict";
27+
exports.__esModule = true;
28+
function foo() {
29+
return null;
30+
}
31+
exports.foo = foo;
32+
//// [file3.js]
33+
"use strict";
34+
exports.__esModule = true;
35+
var file2_1 = require("./file2");
36+
function bar() {
37+
return file2_1.foo();
38+
}
39+
exports.bar = bar;
40+
41+
42+
//// [file1.d.ts]
43+
export declare class Foo {
44+
}
45+
//// [file2.d.ts]
46+
export declare function foo(): import("./file1").Foo;
47+
//// [file3.d.ts]
48+
export declare function bar(): import("./file1").Foo;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
=== tests/cases/compiler/file1.ts ===
2+
export class Foo {}
3+
>Foo : Symbol(Foo, Decl(file1.ts, 0, 0))
4+
5+
=== tests/cases/compiler/file2.ts ===
6+
export function foo(): import("./file1").Foo {
7+
>foo : Symbol(foo, Decl(file2.ts, 0, 0))
8+
>Foo : Symbol(Foo, Decl(file1.ts, 0, 0))
9+
10+
return null as any;
11+
}
12+
=== tests/cases/compiler/file3.ts ===
13+
import {foo} from "./file2";
14+
>foo : Symbol(foo, Decl(file3.ts, 0, 8))
15+
16+
export function bar() {
17+
>bar : Symbol(bar, Decl(file3.ts, 0, 28))
18+
19+
return foo();
20+
>foo : Symbol(foo, Decl(file3.ts, 0, 8))
21+
}
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
=== tests/cases/compiler/file1.ts ===
2+
export class Foo {}
3+
>Foo : Foo
4+
5+
=== tests/cases/compiler/file2.ts ===
6+
export function foo(): import("./file1").Foo {
7+
>foo : () => import("tests/cases/compiler/file1").Foo
8+
>Foo : import("tests/cases/compiler/file1").Foo
9+
10+
return null as any;
11+
>null as any : any
12+
>null : null
13+
}
14+
=== tests/cases/compiler/file3.ts ===
15+
import {foo} from "./file2";
16+
>foo : () => import("tests/cases/compiler/file1").Foo
17+
18+
export function bar() {
19+
>bar : () => import("tests/cases/compiler/file1").Foo
20+
21+
return foo();
22+
>foo() : import("tests/cases/compiler/file1").Foo
23+
>foo : () => import("tests/cases/compiler/file1").Foo
24+
}
25+

tests/baselines/reference/doubleMixinConditionalTypeBaseClassWorks.types

+14-14
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,39 @@ type Constructor = new (...args: any[]) => {};
44
>args : any[]
55

66
const Mixin1 = <C extends Constructor>(Base: C) => class extends Base { private _fooPrivate: {}; }
7-
>Mixin1 : <C extends Constructor>(Base: C) => { new (...args: any[]): (Anonymous class); prototype: <any>.(Anonymous class); } & C
8-
><C extends Constructor>(Base: C) => class extends Base { private _fooPrivate: {}; } : <C extends Constructor>(Base: C) => { new (...args: any[]): (Anonymous class); prototype: <any>.(Anonymous class); } & C
7+
>Mixin1 : <C extends Constructor>(Base: C) => { new (...args: any[]): (Anonymous class); prototype: Mixin1<any>.(Anonymous class); } & C
8+
><C extends Constructor>(Base: C) => class extends Base { private _fooPrivate: {}; } : <C extends Constructor>(Base: C) => { new (...args: any[]): (Anonymous class); prototype: Mixin1<any>.(Anonymous class); } & C
99
>C : C
1010
>Constructor : Constructor
1111
>Base : C
1212
>C : C
13-
>class extends Base { private _fooPrivate: {}; } : { new (...args: any[]): (Anonymous class); prototype: <any>.(Anonymous class); } & C
13+
>class extends Base { private _fooPrivate: {}; } : { new (...args: any[]): (Anonymous class); prototype: Mixin1<any>.(Anonymous class); } & C
1414
>Base : {}
1515
>_fooPrivate : {}
1616

1717
type FooConstructor = typeof Mixin1 extends (a: Constructor) => infer Cls ? Cls : never;
18-
>FooConstructor : { new (...args: any[]): <Constructor>.(Anonymous class); prototype: <any>.(Anonymous class); } & Constructor
19-
>Mixin1 : <C extends Constructor>(Base: C) => { new (...args: any[]): (Anonymous class); prototype: <any>.(Anonymous class); } & C
18+
>FooConstructor : { new (...args: any[]): Mixin1<Constructor>.(Anonymous class); prototype: Mixin1<any>.(Anonymous class); } & Constructor
19+
>Mixin1 : <C extends Constructor>(Base: C) => { new (...args: any[]): (Anonymous class); prototype: Mixin1<any>.(Anonymous class); } & C
2020
>a : Constructor
2121
>Constructor : Constructor
2222
>Cls : Cls
2323
>Cls : Cls
2424

2525
const Mixin2 = <C extends FooConstructor>(Base: C) => class extends Base {};
26-
>Mixin2 : <C extends { new (...args: any[]): <Constructor>.(Anonymous class); prototype: <any>.(Anonymous class); } & Constructor>(Base: C) => { new (...args: any[]): (Anonymous class); prototype: <any>.(Anonymous class); } & C
27-
><C extends FooConstructor>(Base: C) => class extends Base {} : <C extends { new (...args: any[]): <Constructor>.(Anonymous class); prototype: <any>.(Anonymous class); } & Constructor>(Base: C) => { new (...args: any[]): (Anonymous class); prototype: <any>.(Anonymous class); } & C
26+
>Mixin2 : <C extends { new (...args: any[]): Mixin1<Constructor>.(Anonymous class); prototype: Mixin1<any>.(Anonymous class); } & Constructor>(Base: C) => { new (...args: any[]): (Anonymous class); prototype: Mixin2<any>.(Anonymous class); } & C
27+
><C extends FooConstructor>(Base: C) => class extends Base {} : <C extends { new (...args: any[]): Mixin1<Constructor>.(Anonymous class); prototype: Mixin1<any>.(Anonymous class); } & Constructor>(Base: C) => { new (...args: any[]): (Anonymous class); prototype: Mixin2<any>.(Anonymous class); } & C
2828
>C : C
29-
>FooConstructor : { new (...args: any[]): <Constructor>.(Anonymous class); prototype: <any>.(Anonymous class); } & Constructor
29+
>FooConstructor : { new (...args: any[]): Mixin1<Constructor>.(Anonymous class); prototype: Mixin1<any>.(Anonymous class); } & Constructor
3030
>Base : C
3131
>C : C
32-
>class extends Base {} : { new (...args: any[]): (Anonymous class); prototype: <any>.(Anonymous class); } & C
33-
>Base : <Constructor>.(Anonymous class)
32+
>class extends Base {} : { new (...args: any[]): (Anonymous class); prototype: Mixin2<any>.(Anonymous class); } & C
33+
>Base : Mixin1<Constructor>.(Anonymous class)
3434

3535
class C extends Mixin2(Mixin1(Object)) {}
3636
>C : C
37-
>Mixin2(Mixin1(Object)) : <{ new (...args: any[]): <ObjectConstructor>.(Anonymous class); prototype: <any>.(Anonymous class); } & ObjectConstructor>.(Anonymous class) & <ObjectConstructor>.(Anonymous class) & Object
38-
>Mixin2 : <C extends { new (...args: any[]): <Constructor>.(Anonymous class); prototype: <any>.(Anonymous class); } & Constructor>(Base: C) => { new (...args: any[]): (Anonymous class); prototype: <any>.(Anonymous class); } & C
39-
>Mixin1(Object) : { new (...args: any[]): <ObjectConstructor>.(Anonymous class); prototype: <any>.(Anonymous class); } & ObjectConstructor
40-
>Mixin1 : <C extends Constructor>(Base: C) => { new (...args: any[]): (Anonymous class); prototype: <any>.(Anonymous class); } & C
37+
>Mixin2(Mixin1(Object)) : Mixin2<{ new (...args: any[]): Mixin1<ObjectConstructor>.(Anonymous class); prototype: Mixin1<any>.(Anonymous class); } & ObjectConstructor>.(Anonymous class) & Mixin1<ObjectConstructor>.(Anonymous class) & Object
38+
>Mixin2 : <C extends { new (...args: any[]): Mixin1<Constructor>.(Anonymous class); prototype: Mixin1<any>.(Anonymous class); } & Constructor>(Base: C) => { new (...args: any[]): (Anonymous class); prototype: Mixin2<any>.(Anonymous class); } & C
39+
>Mixin1(Object) : { new (...args: any[]): Mixin1<ObjectConstructor>.(Anonymous class); prototype: Mixin1<any>.(Anonymous class); } & ObjectConstructor
40+
>Mixin1 : <C extends Constructor>(Base: C) => { new (...args: any[]): (Anonymous class); prototype: Mixin1<any>.(Anonymous class); } & C
4141
>Object : ObjectConstructor
4242

0 commit comments

Comments
 (0)