Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use import types to refer to declarations in declaration emit #24071

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 93 additions & 60 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

16 changes: 12 additions & 4 deletions src/compiler/transformers/declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,23 @@ namespace ts {
let lateStatementReplacementMap: Map<VisitResult<LateVisibilityPaintedStatement>>;
let suppressNewDiagnosticContexts: boolean;

const host = context.getEmitHost();
const symbolTracker: SymbolTracker = {
trackSymbol,
reportInaccessibleThisError,
reportInaccessibleUniqueSymbolError,
reportPrivateInBaseOfClassExpression
reportPrivateInBaseOfClassExpression,
moduleResolverHost: host,
trackReferencedAmbientModule,
};
let errorNameNode: DeclarationName | undefined;

let currentSourceFile: SourceFile;
let refs: Map<SourceFile>;
const resolver = context.getEmitResolver();
const options = context.getCompilerOptions();
const newLine = getNewLineCharacter(options);
const { noResolve, stripInternal } = options;
const host = context.getEmitHost();
return transformRoot;

function recordTypeReferenceDirectivesIfNecessary(typeReferenceDirectives: string[]): void {
Expand All @@ -63,6 +66,11 @@ namespace ts {
}
}

function trackReferencedAmbientModule(node: ModuleDeclaration) {
const container = getSourceFileOfNode(node);
refs.set("" + getOriginalNodeId(container), container);
}

function handleSymbolAccessibilityError(symbolAccessibilityResult: SymbolAccessibilityResult) {
if (symbolAccessibilityResult.accessibility === SymbolAccessibility.Accessible) {
// Add aliases back onto the possible imports list if they're not there so we can try them again with updated visibility info
Expand Down Expand Up @@ -197,13 +205,13 @@ namespace ts {
lateMarkedStatements = undefined;
lateStatementReplacementMap = createMap();
necessaryTypeRefernces = undefined;
const refs = collectReferences(currentSourceFile, createMap());
refs = collectReferences(currentSourceFile, createMap());
const references: FileReference[] = [];
const outputFilePath = getDirectoryPath(normalizeSlashes(getOutputPathsFor(node, host, /*forceDtsPaths*/ true).declarationFilePath));
const referenceVisitor = mapReferencesIntoArray(references, outputFilePath);
refs.forEach(referenceVisitor);
const statements = visitNodes(node.statements, visitDeclarationStatements);
let combinedStatements = setTextRange(createNodeArray(transformAndReplaceLatePaintedStatements(statements)), node.statements);
refs.forEach(referenceVisitor);
const emittedImports = filter(combinedStatements, isAnyImportSyntax);
if (isExternalModule(node) && (!resultHasExternalModuleIndicator || (needsScopeFixMarker && !resultHasScopeMarker))) {
combinedStatements = setTextRange(createNodeArray([...combinedStatements, createExportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createNamedExports([]), /*moduleSpecifier*/ undefined)]), combinedStatements);
Expand Down
13 changes: 12 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3076,7 +3076,7 @@ namespace ts {
WriteArrayAsGenericType = 1 << 1, // Write Array<T> instead T[]
GenerateNamesForShadowedTypeParams = 1 << 2, // When a type parameter T is shadowing another T, generate a name for it so it can still be referenced
UseStructuralFallback = 1 << 3, // When an alias cannot be named by its symbol, rather than report an error, fallback to a structural printout if possible
// empty space
ForbidIndexedAccessSymbolReferences = 1 << 4, // Forbid references like `I["a"]["b"]` - print `typeof I.a<x>.b<y>` instead
WriteTypeArgumentsOfSignature = 1 << 5, // Write the type arguments instead of type parameters of the signature
UseFullyQualifiedType = 1 << 6, // Write out the fully qualified type name (eg. Module.Type, instead of Type)
UseOnlyExternalAliasing = 1 << 7, // Only use external aliases for a symbol
Expand Down Expand Up @@ -5239,6 +5239,13 @@ namespace ts {
isAtStartOfLine(): boolean;
}

/* @internal */
export interface ModuleNameResolverHost {
getCanonicalFileName(f: string): string;
getCommonSourceDirectory(): string;
getCurrentDirectory(): string;
}

/** @deprecated See comment on SymbolWriter */
// Note: this has non-deprecated internal uses.
export interface SymbolTracker {
Expand All @@ -5249,6 +5256,10 @@ namespace ts {
reportInaccessibleThisError?(): void;
reportPrivateInBaseOfClassExpression?(propertyName: string): void;
reportInaccessibleUniqueSymbolError?(): void;
/* @internal */
moduleResolverHost?: ModuleNameResolverHost;
/* @internal */
trackReferencedAmbientModule?(decl: ModuleDeclaration): void;
}

export interface TextSpan {
Expand Down
13 changes: 7 additions & 6 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2875,11 +2875,11 @@ namespace ts {
};
}

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

export function getExternalModuleNameFromDeclaration(host: EmitHost, resolver: EmitResolver, declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode): string {
export function getExternalModuleNameFromDeclaration(host: ModuleNameResolverHost, resolver: EmitResolver, declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode): string {
const file = resolver.getExternalModuleFileFromDeclaration(declaration);
if (!file || file.isDeclarationFile) {
return undefined;
Expand All @@ -2890,12 +2890,13 @@ namespace ts {
/**
* Resolves a local path to a path which is absolute to the base of the emit
*/
export function getExternalModuleNameFromPath(host: EmitHost, fileName: string): string {
export function getExternalModuleNameFromPath(host: ModuleNameResolverHost, fileName: string, referencePath?: string): string {
const getCanonicalFileName = (f: string) => host.getCanonicalFileName(f);
const dir = toPath(host.getCommonSourceDirectory(), host.getCurrentDirectory(), getCanonicalFileName);
const dir = toPath(referencePath ? getDirectoryPath(referencePath) : host.getCommonSourceDirectory(), host.getCurrentDirectory(), getCanonicalFileName);
const filePath = getNormalizedAbsolutePath(fileName, host.getCurrentDirectory());
const relativePath = getRelativePathToDirectoryOrUrl(dir, filePath, dir, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
return removeFileExtension(relativePath);
const extensionless = removeFileExtension(relativePath);
return referencePath ? ensurePathIsNonModuleName(extensionless) : extensionless;
}

export function getOwnEmitOutputFilePath(sourceFile: SourceFile, host: EmitHost, extension: string) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
=== tests/cases/compiler/Class.ts ===
import { Configurable } from "./Configurable"
>Configurable : <T extends new (...args: any[]) => {}>(base: T) => T
>Configurable : <T extends import("tests/cases/compiler/Configurable").Constructor<{}>>(base: T) => T

export class HiddenClass {}
>HiddenClass : HiddenClass

export class ActualClass extends Configurable(HiddenClass) {}
>ActualClass : ActualClass
>Configurable(HiddenClass) : HiddenClass
>Configurable : <T extends new (...args: any[]) => {}>(base: T) => T
>Configurable : <T extends import("tests/cases/compiler/Configurable").Constructor<{}>>(base: T) => T
>HiddenClass : typeof HiddenClass

=== tests/cases/compiler/Configurable.ts ===
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1885,6 +1885,7 @@ declare namespace ts {
WriteArrayAsGenericType = 2,
GenerateNamesForShadowedTypeParams = 4,
UseStructuralFallback = 8,
ForbidIndexedAccessSymbolReferences = 16,
WriteTypeArgumentsOfSignature = 32,
UseFullyQualifiedType = 64,
UseOnlyExternalAliasing = 128,
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1885,6 +1885,7 @@ declare namespace ts {
WriteArrayAsGenericType = 2,
GenerateNamesForShadowedTypeParams = 4,
UseStructuralFallback = 8,
ForbidIndexedAccessSymbolReferences = 16,
WriteTypeArgumentsOfSignature = 32,
UseFullyQualifiedType = 64,
UseOnlyExternalAliasing = 128,
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/augmentExportEquals5.types
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ declare module "express" {
function e(): e.Express;
>e : typeof e
>e : any
>Express : Express
>Express : e.Express

namespace e {
>e : typeof e
Expand Down
21 changes: 3 additions & 18 deletions tests/baselines/reference/declarationEmitAliasFromIndirectFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,9 @@ exports["default"] = fp.l10ns;

//// [app.d.ts]
declare const _default: {
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];
};
};
ar?: import("./locale").CustomLocale;
bg?: import("./locale").CustomLocale;
} & {
default: {
weekdays: {
shorthand: [string, string, string, string, string, string, string];
longhand: [string, string, string, string, string, string, string];
};
};
default: import("./locale").Locale;
};
export default _default;
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const fp = { l10ns: {} } as FlatpickrFn;
>FlatpickrFn : FlatpickrFn

export default fp.l10ns;
>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]; }; }; }
>fp.l10ns : { ar?: import("tests/cases/compiler/locale").CustomLocale; bg?: import("tests/cases/compiler/locale").CustomLocale; } & { default: import("tests/cases/compiler/locale").Locale; }
>fp : FlatpickrFn
>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]; }; }; }
>l10ns : { ar?: import("tests/cases/compiler/locale").CustomLocale; bg?: import("tests/cases/compiler/locale").CustomLocale; } & { default: import("tests/cases/compiler/locale").Locale; }

Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ exports.v = v;
//// [0.d.ts]
export declare type Data = string | boolean;
//// [1.d.ts]
declare let v: string | boolean;
declare let v: import("./0").Data;
export { v };
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ let obj: Data = true;

=== tests/cases/compiler/1.ts ===
let v = "str" || true;
>v : string | boolean
>v : import("tests/cases/compiler/0").Data
>"str" || true : true | "str"
>"str" : "str"
>true : true

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

Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//// [tests/cases/compiler/declarationsForInferredTypeFromOtherFile.ts] ////

//// [file1.ts]
export class Foo {}
//// [file2.ts]
export function foo(): import("./file1").Foo {
return null as any;
}
//// [file3.ts]
import {foo} from "./file2";
export function bar() {
return foo();
}


//// [file1.js]
"use strict";
exports.__esModule = true;
var Foo = /** @class */ (function () {
function Foo() {
}
return Foo;
}());
exports.Foo = Foo;
//// [file2.js]
"use strict";
exports.__esModule = true;
function foo() {
return null;
}
exports.foo = foo;
//// [file3.js]
"use strict";
exports.__esModule = true;
var file2_1 = require("./file2");
function bar() {
return file2_1.foo();
}
exports.bar = bar;


//// [file1.d.ts]
export declare class Foo {
}
//// [file2.d.ts]
export declare function foo(): import("./file1").Foo;
//// [file3.d.ts]
export declare function bar(): import("./file1").Foo;
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
=== tests/cases/compiler/file1.ts ===
export class Foo {}
>Foo : Symbol(Foo, Decl(file1.ts, 0, 0))

=== tests/cases/compiler/file2.ts ===
export function foo(): import("./file1").Foo {
>foo : Symbol(foo, Decl(file2.ts, 0, 0))
>Foo : Symbol(Foo, Decl(file1.ts, 0, 0))

return null as any;
}
=== tests/cases/compiler/file3.ts ===
import {foo} from "./file2";
>foo : Symbol(foo, Decl(file3.ts, 0, 8))

export function bar() {
>bar : Symbol(bar, Decl(file3.ts, 0, 28))

return foo();
>foo : Symbol(foo, Decl(file3.ts, 0, 8))
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
=== tests/cases/compiler/file1.ts ===
export class Foo {}
>Foo : Foo

=== tests/cases/compiler/file2.ts ===
export function foo(): import("./file1").Foo {
>foo : () => import("tests/cases/compiler/file1").Foo
>Foo : import("tests/cases/compiler/file1").Foo

return null as any;
>null as any : any
>null : null
}
=== tests/cases/compiler/file3.ts ===
import {foo} from "./file2";
>foo : () => import("tests/cases/compiler/file1").Foo

export function bar() {
>bar : () => import("tests/cases/compiler/file1").Foo

return foo();
>foo() : import("tests/cases/compiler/file1").Foo
>foo : () => import("tests/cases/compiler/file1").Foo
}

Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,39 @@ type Constructor = new (...args: any[]) => {};
>args : any[]

const Mixin1 = <C extends Constructor>(Base: C) => class extends Base { private _fooPrivate: {}; }
>Mixin1 : <C extends Constructor>(Base: C) => { new (...args: any[]): (Anonymous class); prototype: <any>.(Anonymous class); } & C
><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
>Mixin1 : <C extends Constructor>(Base: C) => { new (...args: any[]): (Anonymous class); prototype: Mixin1<any>.(Anonymous class); } & C
><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
>C : C
>Constructor : Constructor
>Base : C
>C : C
>class extends Base { private _fooPrivate: {}; } : { new (...args: any[]): (Anonymous class); prototype: <any>.(Anonymous class); } & C
>class extends Base { private _fooPrivate: {}; } : { new (...args: any[]): (Anonymous class); prototype: Mixin1<any>.(Anonymous class); } & C
>Base : {}
>_fooPrivate : {}

type FooConstructor = typeof Mixin1 extends (a: Constructor) => infer Cls ? Cls : never;
>FooConstructor : { new (...args: any[]): <Constructor>.(Anonymous class); prototype: <any>.(Anonymous class); } & Constructor
>Mixin1 : <C extends Constructor>(Base: C) => { new (...args: any[]): (Anonymous class); prototype: <any>.(Anonymous class); } & C
>FooConstructor : { new (...args: any[]): Mixin1<Constructor>.(Anonymous class); prototype: Mixin1<any>.(Anonymous class); } & Constructor
>Mixin1 : <C extends Constructor>(Base: C) => { new (...args: any[]): (Anonymous class); prototype: Mixin1<any>.(Anonymous class); } & C
>a : Constructor
>Constructor : Constructor
>Cls : Cls
>Cls : Cls

const Mixin2 = <C extends FooConstructor>(Base: C) => class extends Base {};
>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
><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
>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
><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
>C : C
>FooConstructor : { new (...args: any[]): <Constructor>.(Anonymous class); prototype: <any>.(Anonymous class); } & Constructor
>FooConstructor : { new (...args: any[]): Mixin1<Constructor>.(Anonymous class); prototype: Mixin1<any>.(Anonymous class); } & Constructor
>Base : C
>C : C
>class extends Base {} : { new (...args: any[]): (Anonymous class); prototype: <any>.(Anonymous class); } & C
>Base : <Constructor>.(Anonymous class)
>class extends Base {} : { new (...args: any[]): (Anonymous class); prototype: Mixin2<any>.(Anonymous class); } & C
>Base : Mixin1<Constructor>.(Anonymous class)

class C extends Mixin2(Mixin1(Object)) {}
>C : C
>Mixin2(Mixin1(Object)) : <{ new (...args: any[]): <ObjectConstructor>.(Anonymous class); prototype: <any>.(Anonymous class); } & ObjectConstructor>.(Anonymous class) & <ObjectConstructor>.(Anonymous class) & Object
>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
>Mixin1(Object) : { new (...args: any[]): <ObjectConstructor>.(Anonymous class); prototype: <any>.(Anonymous class); } & ObjectConstructor
>Mixin1 : <C extends Constructor>(Base: C) => { new (...args: any[]): (Anonymous class); prototype: <any>.(Anonymous class); } & C
>Mixin2(Mixin1(Object)) : Mixin2<{ new (...args: any[]): Mixin1<ObjectConstructor>.(Anonymous class); prototype: Mixin1<any>.(Anonymous class); } & ObjectConstructor>.(Anonymous class) & Mixin1<ObjectConstructor>.(Anonymous class) & Object
>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
>Mixin1(Object) : { new (...args: any[]): Mixin1<ObjectConstructor>.(Anonymous class); prototype: Mixin1<any>.(Anonymous class); } & ObjectConstructor
>Mixin1 : <C extends Constructor>(Base: C) => { new (...args: any[]): (Anonymous class); prototype: Mixin1<any>.(Anonymous class); } & C
>Object : ObjectConstructor

Loading