mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Avoid trying to emit anonymous classish/expando functions as assignments (#55472)
This commit is contained in:
+23
-6
@@ -8533,11 +8533,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
...oldcontext,
|
||||
usedSymbolNames: new Set(oldcontext.usedSymbolNames),
|
||||
remappedSymbolNames: new Map(),
|
||||
remappedSymbolReferences: new Map(oldcontext.remappedSymbolReferences?.entries()),
|
||||
tracker: undefined!,
|
||||
};
|
||||
const tracker: SymbolTracker = {
|
||||
...oldcontext.tracker.inner,
|
||||
trackSymbol: (sym, decl, meaning) => {
|
||||
if (context.remappedSymbolNames?.has(getSymbolId(sym))) return false; // If the context has a remapped name for the symbol, it *should* mean it's been made visible
|
||||
const accessibleResult = isSymbolAccessible(sym, decl, meaning, /*shouldComputeAliasesToMakeVisible*/ false);
|
||||
if (accessibleResult.accessibility === SymbolAccessibility.Accessible) {
|
||||
// Lookup the root symbol of the chain of refs we'll use to access it and serialize it
|
||||
@@ -8790,9 +8792,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// If it's a property: emit `export default _default` with a `_default` prop
|
||||
// If it's a class/interface/function: emit a class/interface/function with a `default` modifier
|
||||
// These forms can merge, eg (`export default 12; export default interface A {}`)
|
||||
function serializeSymbolWorker(symbol: Symbol, isPrivate: boolean, propertyAsAlias: boolean): void {
|
||||
const symbolName = unescapeLeadingUnderscores(symbol.escapedName);
|
||||
const isDefault = symbol.escapedName === InternalSymbolName.Default;
|
||||
function serializeSymbolWorker(symbol: Symbol, isPrivate: boolean, propertyAsAlias: boolean, escapedSymbolName = symbol.escapedName): void {
|
||||
const symbolName = unescapeLeadingUnderscores(escapedSymbolName);
|
||||
const isDefault = escapedSymbolName === InternalSymbolName.Default;
|
||||
if (isPrivate && !(context.flags & NodeBuilderFlags.AllowAnonymousIdentifier) && isStringANonContextualKeyword(symbolName) && !isDefault) {
|
||||
// Oh no. We cannot use this symbol's name as it's name... It's likely some jsdoc had an invalid name like `export` or `default` :(
|
||||
context.encounteredError = true;
|
||||
@@ -8811,7 +8813,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
const modifierFlags = (!isPrivate ? ModifierFlags.Export : 0) | (isDefault && !needsPostExportDefault ? ModifierFlags.Default : 0);
|
||||
const isConstMergedWithNS = symbol.flags & SymbolFlags.Module &&
|
||||
symbol.flags & (SymbolFlags.BlockScopedVariable | SymbolFlags.FunctionScopedVariable | SymbolFlags.Property) &&
|
||||
symbol.escapedName !== InternalSymbolName.ExportEquals;
|
||||
escapedSymbolName !== InternalSymbolName.ExportEquals;
|
||||
const isConstMergedWithNSPrintableAsSignatureMerge = isConstMergedWithNS && isTypeRepresentableAsFunctionNamespaceMerge(getTypeOfSymbol(symbol), symbol);
|
||||
if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method) || isConstMergedWithNSPrintableAsSignatureMerge) {
|
||||
serializeAsFunctionNamespaceMerge(getTypeOfSymbol(symbol), symbol, getInternalSymbolName(symbol, symbolName), modifierFlags);
|
||||
@@ -8823,7 +8825,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// symbol of name `export=` which needs to be handled like an alias. It's not great, but it is what it is.
|
||||
if (
|
||||
symbol.flags & (SymbolFlags.BlockScopedVariable | SymbolFlags.FunctionScopedVariable | SymbolFlags.Property | SymbolFlags.Accessor)
|
||||
&& symbol.escapedName !== InternalSymbolName.ExportEquals
|
||||
&& escapedSymbolName !== InternalSymbolName.ExportEquals
|
||||
&& !(symbol.flags & SymbolFlags.Prototype)
|
||||
&& !(symbol.flags & SymbolFlags.Class)
|
||||
&& !(symbol.flags & SymbolFlags.Method)
|
||||
@@ -8839,7 +8841,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
else {
|
||||
const type = getTypeOfSymbol(symbol);
|
||||
const localName = getInternalSymbolName(symbol, symbolName);
|
||||
if (!(symbol.flags & SymbolFlags.Function) && isTypeRepresentableAsFunctionNamespaceMerge(type, symbol)) {
|
||||
if (type.symbol && type.symbol !== symbol && type.symbol.flags & SymbolFlags.Function && some(type.symbol.declarations, isFunctionExpressionOrArrowFunction) && (type.symbol.members?.size || type.symbol.exports?.size)) {
|
||||
// assignment of a anonymous expando/class-like function, the func/ns/merge branch below won't trigger,
|
||||
// and the assignment form has to reference the unreachable anonymous type so will error.
|
||||
// Instead, serialize the type's symbol, but with the current symbol's name, rather than the anonymous one.
|
||||
if (!context.remappedSymbolReferences) {
|
||||
context.remappedSymbolReferences = new Map();
|
||||
}
|
||||
context.remappedSymbolReferences.set(getSymbolId(type.symbol), symbol); // save name remapping as local name for target symbol
|
||||
serializeSymbolWorker(type.symbol, isPrivate, propertyAsAlias, escapedSymbolName);
|
||||
context.remappedSymbolReferences.delete(getSymbolId(type.symbol));
|
||||
}
|
||||
else if (!(symbol.flags & SymbolFlags.Function) && isTypeRepresentableAsFunctionNamespaceMerge(type, symbol)) {
|
||||
// If the type looks like a function declaration + ns could represent it, and it's type is sourced locally, rewrite it into a function declaration + ns
|
||||
serializeAsFunctionNamespaceMerge(type, symbol, localName, modifierFlags);
|
||||
}
|
||||
@@ -10159,6 +10172,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
* It will also use a representation of a number as written instead of a decimal form, e.g. `0o11` instead of `9`.
|
||||
*/
|
||||
function getNameOfSymbolAsWritten(symbol: Symbol, context?: NodeBuilderContext): string {
|
||||
if (context?.remappedSymbolReferences?.has(getSymbolId(symbol))) {
|
||||
symbol = context.remappedSymbolReferences.get(getSymbolId(symbol))!;
|
||||
}
|
||||
if (
|
||||
context && symbol.escapedName === InternalSymbolName.Default && !(context.flags & NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope) &&
|
||||
// If it's not the first part of an entity name, it must print as `default`
|
||||
@@ -49986,6 +50002,7 @@ interface NodeBuilderContext {
|
||||
typeParameterNamesByTextNextNameCount?: Map<string, number>;
|
||||
usedSymbolNames?: Set<string>;
|
||||
remappedSymbolNames?: Map<SymbolId, string>;
|
||||
remappedSymbolReferences?: Map<SymbolId, Symbol>;
|
||||
reverseMappedStack?: ReverseMappedSymbol[];
|
||||
}
|
||||
|
||||
|
||||
@@ -228,6 +228,14 @@ type Input = {
|
||||
timer: Timer;
|
||||
hook: Hook;
|
||||
};
|
||||
/**
|
||||
* Imports
|
||||
*/
|
||||
type Timer = import("./timer");
|
||||
/**
|
||||
* Imports
|
||||
*/
|
||||
type Hook = import("./hook");
|
||||
/**
|
||||
* Imports
|
||||
*/
|
||||
@@ -239,14 +247,6 @@ type State = {
|
||||
timer: Timer;
|
||||
hook: Hook;
|
||||
};
|
||||
/**
|
||||
* Imports
|
||||
*/
|
||||
type Timer = import("./timer");
|
||||
/**
|
||||
* Imports
|
||||
*/
|
||||
type Hook = import("./hook");
|
||||
//// [hook.d.ts]
|
||||
export = Hook;
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
//// [tests/cases/compiler/jsDeclarationsGlobalFileConstFunction.ts] ////
|
||||
|
||||
//// [file.js]
|
||||
const SomeConstructor = function () {
|
||||
this.x = 1;
|
||||
};
|
||||
|
||||
const SomeConstructor2 = function () {
|
||||
};
|
||||
SomeConstructor2.staticMember = "str";
|
||||
|
||||
const SomeConstructor3 = function () {
|
||||
this.x = 1;
|
||||
};
|
||||
SomeConstructor3.staticMember = "str";
|
||||
|
||||
|
||||
|
||||
|
||||
//// [file.d.ts]
|
||||
declare function SomeConstructor(): void;
|
||||
declare class SomeConstructor {
|
||||
x: number;
|
||||
}
|
||||
declare function SomeConstructor2(): void;
|
||||
declare namespace SomeConstructor2 {
|
||||
let staticMember: string;
|
||||
}
|
||||
declare function SomeConstructor3(): void;
|
||||
declare namespace SomeConstructor3 {
|
||||
let staticMember_1: string;
|
||||
export { staticMember_1 as staticMember };
|
||||
}
|
||||
declare class SomeConstructor3 {
|
||||
x: number;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
//// [tests/cases/compiler/jsDeclarationsGlobalFileConstFunction.ts] ////
|
||||
|
||||
=== file.js ===
|
||||
const SomeConstructor = function () {
|
||||
>SomeConstructor : Symbol(SomeConstructor, Decl(file.js, 0, 5))
|
||||
|
||||
this.x = 1;
|
||||
>this.x : Symbol(SomeConstructor.x, Decl(file.js, 0, 37))
|
||||
>this : Symbol(SomeConstructor, Decl(file.js, 0, 23))
|
||||
>x : Symbol(SomeConstructor.x, Decl(file.js, 0, 37))
|
||||
|
||||
};
|
||||
|
||||
const SomeConstructor2 = function () {
|
||||
>SomeConstructor2 : Symbol(SomeConstructor2, Decl(file.js, 4, 5), Decl(file.js, 5, 2))
|
||||
|
||||
};
|
||||
SomeConstructor2.staticMember = "str";
|
||||
>SomeConstructor2.staticMember : Symbol(SomeConstructor2.staticMember, Decl(file.js, 5, 2))
|
||||
>SomeConstructor2 : Symbol(SomeConstructor2, Decl(file.js, 4, 5), Decl(file.js, 5, 2))
|
||||
>staticMember : Symbol(SomeConstructor2.staticMember, Decl(file.js, 5, 2))
|
||||
|
||||
const SomeConstructor3 = function () {
|
||||
>SomeConstructor3 : Symbol(SomeConstructor3, Decl(file.js, 8, 5), Decl(file.js, 10, 2))
|
||||
|
||||
this.x = 1;
|
||||
>this.x : Symbol(SomeConstructor3.x, Decl(file.js, 8, 38))
|
||||
>this : Symbol(SomeConstructor3, Decl(file.js, 8, 24))
|
||||
>x : Symbol(SomeConstructor3.x, Decl(file.js, 8, 38))
|
||||
|
||||
};
|
||||
SomeConstructor3.staticMember = "str";
|
||||
>SomeConstructor3.staticMember : Symbol(SomeConstructor3.staticMember, Decl(file.js, 10, 2))
|
||||
>SomeConstructor3 : Symbol(SomeConstructor3, Decl(file.js, 8, 5), Decl(file.js, 10, 2))
|
||||
>staticMember : Symbol(SomeConstructor3.staticMember, Decl(file.js, 10, 2))
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
//// [tests/cases/compiler/jsDeclarationsGlobalFileConstFunction.ts] ////
|
||||
|
||||
=== file.js ===
|
||||
const SomeConstructor = function () {
|
||||
>SomeConstructor : typeof SomeConstructor
|
||||
>function () { this.x = 1;} : typeof SomeConstructor
|
||||
|
||||
this.x = 1;
|
||||
>this.x = 1 : 1
|
||||
>this.x : any
|
||||
>this : this
|
||||
>x : any
|
||||
>1 : 1
|
||||
|
||||
};
|
||||
|
||||
const SomeConstructor2 = function () {
|
||||
>SomeConstructor2 : { (): void; staticMember: string; }
|
||||
>function () {} : { (): void; staticMember: string; }
|
||||
|
||||
};
|
||||
SomeConstructor2.staticMember = "str";
|
||||
>SomeConstructor2.staticMember = "str" : "str"
|
||||
>SomeConstructor2.staticMember : string
|
||||
>SomeConstructor2 : { (): void; staticMember: string; }
|
||||
>staticMember : string
|
||||
>"str" : "str"
|
||||
|
||||
const SomeConstructor3 = function () {
|
||||
>SomeConstructor3 : typeof SomeConstructor3
|
||||
>function () { this.x = 1;} : typeof SomeConstructor3
|
||||
|
||||
this.x = 1;
|
||||
>this.x = 1 : 1
|
||||
>this.x : any
|
||||
>this : this
|
||||
>x : any
|
||||
>1 : 1
|
||||
|
||||
};
|
||||
SomeConstructor3.staticMember = "str";
|
||||
>SomeConstructor3.staticMember = "str" : "str"
|
||||
>SomeConstructor3.staticMember : string
|
||||
>SomeConstructor3 : typeof SomeConstructor3
|
||||
>staticMember : string
|
||||
>"str" : "str"
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
//// [tests/cases/compiler/jsDeclarationsGlobalFileConstFunctionNamed.ts] ////
|
||||
|
||||
//// [file.js]
|
||||
const SomeConstructor = function Named() {
|
||||
this.x = 1;
|
||||
};
|
||||
|
||||
const SomeConstructor2 = function Named() {
|
||||
};
|
||||
SomeConstructor2.staticMember = "str";
|
||||
|
||||
const SomeConstructor3 = function Named() {
|
||||
this.x = 1;
|
||||
};
|
||||
SomeConstructor3.staticMember = "str";
|
||||
|
||||
const SelfReference = function Named() {
|
||||
if (!(this instanceof Named)) return new Named();
|
||||
this.x = 1;
|
||||
}
|
||||
SelfReference.staticMember = "str";
|
||||
|
||||
|
||||
|
||||
|
||||
//// [file.d.ts]
|
||||
declare function SomeConstructor(): void;
|
||||
declare class SomeConstructor {
|
||||
x: number;
|
||||
}
|
||||
declare function SomeConstructor2(): void;
|
||||
declare namespace SomeConstructor2 {
|
||||
let staticMember: string;
|
||||
}
|
||||
declare function SomeConstructor3(): void;
|
||||
declare namespace SomeConstructor3 {
|
||||
let staticMember_1: string;
|
||||
export { staticMember_1 as staticMember };
|
||||
}
|
||||
declare class SomeConstructor3 {
|
||||
x: number;
|
||||
}
|
||||
declare function SelfReference(): SelfReference;
|
||||
declare namespace SelfReference {
|
||||
let staticMember_2: string;
|
||||
export { staticMember_2 as staticMember };
|
||||
}
|
||||
declare class SelfReference {
|
||||
x: number;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
//// [tests/cases/compiler/jsDeclarationsGlobalFileConstFunctionNamed.ts] ////
|
||||
|
||||
=== file.js ===
|
||||
const SomeConstructor = function Named() {
|
||||
>SomeConstructor : Symbol(SomeConstructor, Decl(file.js, 0, 5))
|
||||
>Named : Symbol(Named, Decl(file.js, 0, 23))
|
||||
|
||||
this.x = 1;
|
||||
>this.x : Symbol(Named.x, Decl(file.js, 0, 42))
|
||||
>this : Symbol(Named, Decl(file.js, 0, 23))
|
||||
>x : Symbol(Named.x, Decl(file.js, 0, 42))
|
||||
|
||||
};
|
||||
|
||||
const SomeConstructor2 = function Named() {
|
||||
>SomeConstructor2 : Symbol(SomeConstructor2, Decl(file.js, 4, 5), Decl(file.js, 5, 2))
|
||||
>Named : Symbol(Named, Decl(file.js, 4, 24))
|
||||
|
||||
};
|
||||
SomeConstructor2.staticMember = "str";
|
||||
>SomeConstructor2.staticMember : Symbol(SomeConstructor2.staticMember, Decl(file.js, 5, 2))
|
||||
>SomeConstructor2 : Symbol(SomeConstructor2, Decl(file.js, 4, 5), Decl(file.js, 5, 2))
|
||||
>staticMember : Symbol(SomeConstructor2.staticMember, Decl(file.js, 5, 2))
|
||||
|
||||
const SomeConstructor3 = function Named() {
|
||||
>SomeConstructor3 : Symbol(SomeConstructor3, Decl(file.js, 8, 5), Decl(file.js, 10, 2))
|
||||
>Named : Symbol(Named, Decl(file.js, 8, 24))
|
||||
|
||||
this.x = 1;
|
||||
>this.x : Symbol(Named.x, Decl(file.js, 8, 43))
|
||||
>this : Symbol(Named, Decl(file.js, 8, 24))
|
||||
>x : Symbol(Named.x, Decl(file.js, 8, 43))
|
||||
|
||||
};
|
||||
SomeConstructor3.staticMember = "str";
|
||||
>SomeConstructor3.staticMember : Symbol(SomeConstructor3.staticMember, Decl(file.js, 10, 2))
|
||||
>SomeConstructor3 : Symbol(SomeConstructor3, Decl(file.js, 8, 5), Decl(file.js, 10, 2))
|
||||
>staticMember : Symbol(SomeConstructor3.staticMember, Decl(file.js, 10, 2))
|
||||
|
||||
const SelfReference = function Named() {
|
||||
>SelfReference : Symbol(SelfReference, Decl(file.js, 13, 5), Decl(file.js, 16, 1))
|
||||
>Named : Symbol(Named, Decl(file.js, 13, 21))
|
||||
|
||||
if (!(this instanceof Named)) return new Named();
|
||||
>this : Symbol(Named, Decl(file.js, 13, 21))
|
||||
>Named : Symbol(Named, Decl(file.js, 13, 21))
|
||||
>Named : Symbol(Named, Decl(file.js, 13, 21))
|
||||
|
||||
this.x = 1;
|
||||
>this.x : Symbol(Named.x, Decl(file.js, 14, 53))
|
||||
>this : Symbol(Named, Decl(file.js, 13, 21))
|
||||
>x : Symbol(Named.x, Decl(file.js, 14, 53))
|
||||
}
|
||||
SelfReference.staticMember = "str";
|
||||
>SelfReference.staticMember : Symbol(SelfReference.staticMember, Decl(file.js, 16, 1))
|
||||
>SelfReference : Symbol(SelfReference, Decl(file.js, 13, 5), Decl(file.js, 16, 1))
|
||||
>staticMember : Symbol(SelfReference.staticMember, Decl(file.js, 16, 1))
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
//// [tests/cases/compiler/jsDeclarationsGlobalFileConstFunctionNamed.ts] ////
|
||||
|
||||
=== file.js ===
|
||||
const SomeConstructor = function Named() {
|
||||
>SomeConstructor : typeof Named
|
||||
>function Named() { this.x = 1;} : typeof Named
|
||||
>Named : typeof Named
|
||||
|
||||
this.x = 1;
|
||||
>this.x = 1 : 1
|
||||
>this.x : any
|
||||
>this : this
|
||||
>x : any
|
||||
>1 : 1
|
||||
|
||||
};
|
||||
|
||||
const SomeConstructor2 = function Named() {
|
||||
>SomeConstructor2 : { (): void; staticMember: string; }
|
||||
>function Named() {} : { (): void; staticMember: string; }
|
||||
>Named : { (): void; staticMember: string; }
|
||||
|
||||
};
|
||||
SomeConstructor2.staticMember = "str";
|
||||
>SomeConstructor2.staticMember = "str" : "str"
|
||||
>SomeConstructor2.staticMember : string
|
||||
>SomeConstructor2 : { (): void; staticMember: string; }
|
||||
>staticMember : string
|
||||
>"str" : "str"
|
||||
|
||||
const SomeConstructor3 = function Named() {
|
||||
>SomeConstructor3 : typeof Named
|
||||
>function Named() { this.x = 1;} : typeof Named
|
||||
>Named : typeof Named
|
||||
|
||||
this.x = 1;
|
||||
>this.x = 1 : 1
|
||||
>this.x : any
|
||||
>this : this
|
||||
>x : any
|
||||
>1 : 1
|
||||
|
||||
};
|
||||
SomeConstructor3.staticMember = "str";
|
||||
>SomeConstructor3.staticMember = "str" : "str"
|
||||
>SomeConstructor3.staticMember : string
|
||||
>SomeConstructor3 : typeof Named
|
||||
>staticMember : string
|
||||
>"str" : "str"
|
||||
|
||||
const SelfReference = function Named() {
|
||||
>SelfReference : typeof Named
|
||||
>function Named() { if (!(this instanceof Named)) return new Named(); this.x = 1;} : typeof Named
|
||||
>Named : typeof Named
|
||||
|
||||
if (!(this instanceof Named)) return new Named();
|
||||
>!(this instanceof Named) : boolean
|
||||
>(this instanceof Named) : boolean
|
||||
>this instanceof Named : boolean
|
||||
>this : this
|
||||
>Named : typeof Named
|
||||
>new Named() : Named
|
||||
>Named : typeof Named
|
||||
|
||||
this.x = 1;
|
||||
>this.x = 1 : 1
|
||||
>this.x : any
|
||||
>this : this
|
||||
>x : any
|
||||
>1 : 1
|
||||
}
|
||||
SelfReference.staticMember = "str";
|
||||
>SelfReference.staticMember = "str" : "str"
|
||||
>SelfReference.staticMember : string
|
||||
>SelfReference : typeof Named
|
||||
>staticMember : string
|
||||
>"str" : "str"
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// @checkJs: true
|
||||
// @declaration: true
|
||||
// @emitDeclarationOnly: true
|
||||
// @filename: file.js
|
||||
const SomeConstructor = function () {
|
||||
this.x = 1;
|
||||
};
|
||||
|
||||
const SomeConstructor2 = function () {
|
||||
};
|
||||
SomeConstructor2.staticMember = "str";
|
||||
|
||||
const SomeConstructor3 = function () {
|
||||
this.x = 1;
|
||||
};
|
||||
SomeConstructor3.staticMember = "str";
|
||||
@@ -0,0 +1,22 @@
|
||||
// @checkJs: true
|
||||
// @declaration: true
|
||||
// @emitDeclarationOnly: true
|
||||
// @filename: file.js
|
||||
const SomeConstructor = function Named() {
|
||||
this.x = 1;
|
||||
};
|
||||
|
||||
const SomeConstructor2 = function Named() {
|
||||
};
|
||||
SomeConstructor2.staticMember = "str";
|
||||
|
||||
const SomeConstructor3 = function Named() {
|
||||
this.x = 1;
|
||||
};
|
||||
SomeConstructor3.staticMember = "str";
|
||||
|
||||
const SelfReference = function Named() {
|
||||
if (!(this instanceof Named)) return new Named();
|
||||
this.x = 1;
|
||||
}
|
||||
SelfReference.staticMember = "str";
|
||||
Reference in New Issue
Block a user