Skip to content

Commit

Permalink
Ensure generated property names for methods named "new" are quoted (#…
Browse files Browse the repository at this point in the history
…55750)

Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
  • Loading branch information
jakebailey and Andarist authored Sep 15, 2023
1 parent b3770e7 commit 9cbcf01
Show file tree
Hide file tree
Showing 22 changed files with 365 additions and 218 deletions.
9 changes: 5 additions & 4 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8213,16 +8213,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function getPropertyNameNodeForSymbol(symbol: Symbol, context: NodeBuilderContext) {
const stringNamed = !!length(symbol.declarations) && every(symbol.declarations, isStringNamed);
const singleQuote = !!length(symbol.declarations) && every(symbol.declarations, isSingleQuotedStringNamed);
const fromNameType = getPropertyNameNodeForSymbolFromNameType(symbol, context, singleQuote, stringNamed);
const isMethod = !!(symbol.flags & SymbolFlags.Method);
const fromNameType = getPropertyNameNodeForSymbolFromNameType(symbol, context, singleQuote, stringNamed, isMethod);
if (fromNameType) {
return fromNameType;
}
const rawName = unescapeLeadingUnderscores(symbol.escapedName);
return createPropertyNameNodeForIdentifierOrLiteral(rawName, getEmitScriptTarget(compilerOptions), singleQuote, stringNamed);
return createPropertyNameNodeForIdentifierOrLiteral(rawName, getEmitScriptTarget(compilerOptions), singleQuote, stringNamed, isMethod);
}

// See getNameForSymbolFromNameType for a stringy equivalent
function getPropertyNameNodeForSymbolFromNameType(symbol: Symbol, context: NodeBuilderContext, singleQuote?: boolean, stringNamed?: boolean) {
function getPropertyNameNodeForSymbolFromNameType(symbol: Symbol, context: NodeBuilderContext, singleQuote: boolean, stringNamed: boolean, isMethod: boolean) {
const nameType = getSymbolLinks(symbol).nameType;
if (nameType) {
if (nameType.flags & TypeFlags.StringOrNumberLiteral) {
Expand All @@ -8233,7 +8234,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (isNumericLiteralName(name) && startsWith(name, "-")) {
return factory.createComputedPropertyName(factory.createPrefixUnaryExpression(SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(+name))));
}
return createPropertyNameNodeForIdentifierOrLiteral(name, getEmitScriptTarget(compilerOptions));
return createPropertyNameNodeForIdentifierOrLiteral(name, getEmitScriptTarget(compilerOptions), singleQuote, stringNamed, isMethod);
}
if (nameType.flags & TypeFlags.UniqueESSymbol) {
return factory.createComputedPropertyName(symbolToExpression((nameType as UniqueESSymbolType).symbol, context, SymbolFlags.Value));
Expand Down
7 changes: 4 additions & 3 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10216,9 +10216,10 @@ export function isNumericLiteralName(name: string | __String) {
}

/** @internal */
export function createPropertyNameNodeForIdentifierOrLiteral(name: string, target: ScriptTarget, singleQuote?: boolean, stringNamed?: boolean) {
return isIdentifierText(name, target) ? factory.createIdentifier(name) :
!stringNamed && isNumericLiteralName(name) && +name >= 0 ? factory.createNumericLiteral(+name) :
export function createPropertyNameNodeForIdentifierOrLiteral(name: string, target: ScriptTarget, singleQuote: boolean, stringNamed: boolean, isMethod: boolean) {
const isMethodNamedNew = isMethod && name === "new";
return !isMethodNamedNew && isIdentifierText(name, target) ? factory.createIdentifier(name) :
!stringNamed && !isMethodNamedNew && isNumericLiteralName(name) && +name >= 0 ? factory.createNumericLiteral(+name) :
factory.createStringLiteral(name, !!singleQuote);
}

Expand Down
3 changes: 2 additions & 1 deletion src/services/codefixes/fixAddMissingMember.ts
Original file line number Diff line number Diff line change
Expand Up @@ -772,7 +772,8 @@ function createPropertyNameFromSymbol(symbol: Symbol, target: ScriptTarget, quot
const prop = checker.symbolToNode(symbol, SymbolFlags.Value, /*enclosingDeclaration*/ undefined, NodeBuilderFlags.WriteComputedProps);
if (prop && isComputedPropertyName(prop)) return prop;
}
return createPropertyNameNodeForIdentifierOrLiteral(symbol.name, target, quotePreference === QuotePreference.Single);
// We're using these nodes as property names in an object literal; no need to quote names when not needed.
return createPropertyNameNodeForIdentifierOrLiteral(symbol.name, target, quotePreference === QuotePreference.Single, /*stringNamed*/ false, /*isMethod*/ false);
}

function findScope(node: Node) {
Expand Down
43 changes: 43 additions & 0 deletions tests/baselines/reference/emitMethodCalledNew.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//// [tests/cases/compiler/emitMethodCalledNew.ts] ////

//// [emitMethodCalledNew.ts]
// https://github.com/microsoft/TypeScript/issues/55075

export const a = {
new(x: number) { return x + 1 }
}
export const b = {
"new"(x: number) { return x + 1 }
}
export const c = {
["new"](x: number) { return x + 1 }
}


//// [emitMethodCalledNew.js]
"use strict";
// https://github.com/microsoft/TypeScript/issues/55075
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.c = exports.b = exports.a = void 0;
exports.a = {
new: function (x) { return x + 1; }
};
exports.b = {
"new": function (x) { return x + 1; }
};
exports.c = (_a = {},
_a["new"] = function (x) { return x + 1; },
_a);


//// [emitMethodCalledNew.d.ts]
export declare const a: {
"new"(x: number): number;
};
export declare const b: {
"new"(x: number): number;
};
export declare const c: {
"new"(x: number): number;
};
31 changes: 31 additions & 0 deletions tests/baselines/reference/emitMethodCalledNew.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//// [tests/cases/compiler/emitMethodCalledNew.ts] ////

=== emitMethodCalledNew.ts ===
// https://github.com/microsoft/TypeScript/issues/55075

export const a = {
>a : Symbol(a, Decl(emitMethodCalledNew.ts, 2, 12))

new(x: number) { return x + 1 }
>new : Symbol(new, Decl(emitMethodCalledNew.ts, 2, 18))
>x : Symbol(x, Decl(emitMethodCalledNew.ts, 3, 6))
>x : Symbol(x, Decl(emitMethodCalledNew.ts, 3, 6))
}
export const b = {
>b : Symbol(b, Decl(emitMethodCalledNew.ts, 5, 12))

"new"(x: number) { return x + 1 }
>"new" : Symbol("new", Decl(emitMethodCalledNew.ts, 5, 18))
>x : Symbol(x, Decl(emitMethodCalledNew.ts, 6, 8))
>x : Symbol(x, Decl(emitMethodCalledNew.ts, 6, 8))
}
export const c = {
>c : Symbol(c, Decl(emitMethodCalledNew.ts, 8, 12))

["new"](x: number) { return x + 1 }
>["new"] : Symbol(["new"], Decl(emitMethodCalledNew.ts, 8, 18))
>"new" : Symbol(["new"], Decl(emitMethodCalledNew.ts, 8, 18))
>x : Symbol(x, Decl(emitMethodCalledNew.ts, 9, 10))
>x : Symbol(x, Decl(emitMethodCalledNew.ts, 9, 10))
}

40 changes: 40 additions & 0 deletions tests/baselines/reference/emitMethodCalledNew.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//// [tests/cases/compiler/emitMethodCalledNew.ts] ////

=== emitMethodCalledNew.ts ===
// https://github.com/microsoft/TypeScript/issues/55075

export const a = {
>a : { "new"(x: number): number; }
>{ new(x: number) { return x + 1 }} : { "new"(x: number): number; }

new(x: number) { return x + 1 }
>new : (x: number) => number
>x : number
>x + 1 : number
>x : number
>1 : 1
}
export const b = {
>b : { "new"(x: number): number; }
>{ "new"(x: number) { return x + 1 }} : { "new"(x: number): number; }

"new"(x: number) { return x + 1 }
>"new" : (x: number) => number
>x : number
>x + 1 : number
>x : number
>1 : 1
}
export const c = {
>c : { "new"(x: number): number; }
>{ ["new"](x: number) { return x + 1 }} : { "new"(x: number): number; }

["new"](x: number) { return x + 1 }
>["new"] : (x: number) => number
>"new" : "new"
>x : number
>x + 1 : number
>x : number
>1 : 1
}

Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ var a: { new(x: Date): string }
>x : Date

var b = { new(x: RegExp) { return ''; } }; // not a construct signature, function called new
>b : { new(x: RegExp): string; }
>{ new(x: RegExp) { return ''; } } : { new(x: RegExp): string; }
>b : { "new"(x: RegExp): string; }
>{ new(x: RegExp) { return ''; } } : { "new"(x: RegExp): string; }
>new : (x: RegExp) => string
>x : RegExp
>'' : ""
Expand Down Expand Up @@ -89,17 +89,17 @@ function foo3(x: any) { }
>x : any

function foo4(x: typeof b);
>foo4 : { (x: typeof b): any; (x: { new(x: RegExp): string; }): any; }
>x : { new(x: RegExp): string; }
>b : { new(x: RegExp): string; }
>foo4 : { (x: typeof b): any; (x: { "new"(x: RegExp): string; }): any; }
>x : { "new"(x: RegExp): string; }
>b : { "new"(x: RegExp): string; }

function foo4(x: typeof b); // error
>foo4 : { (x: { new(x: RegExp): string; }): any; (x: typeof b): any; }
>x : { new(x: RegExp): string; }
>b : { new(x: RegExp): string; }
>foo4 : { (x: { "new"(x: RegExp): string; }): any; (x: typeof b): any; }
>x : { "new"(x: RegExp): string; }
>b : { "new"(x: RegExp): string; }

function foo4(x: any) { }
>foo4 : { (x: { new(x: RegExp): string; }): any; (x: { new(x: RegExp): string; }): any; }
>foo4 : { (x: { "new"(x: RegExp): string; }): any; (x: { "new"(x: RegExp): string; }): any; }
>x : any

function foo8(x: B);
Expand Down Expand Up @@ -140,16 +140,16 @@ function foo10(x: any) { }
>x : any

function foo11(x: B);
>foo11 : { (x: B): any; (x: { new(x: RegExp): string; }): any; }
>foo11 : { (x: B): any; (x: { "new"(x: RegExp): string; }): any; }
>x : B

function foo11(x: typeof b); // ok
>foo11 : { (x: B): any; (x: typeof b): any; }
>x : { new(x: RegExp): string; }
>b : { new(x: RegExp): string; }
>x : { "new"(x: RegExp): string; }
>b : { "new"(x: RegExp): string; }

function foo11(x: any) { }
>foo11 : { (x: B): any; (x: { new(x: RegExp): string; }): any; }
>foo11 : { (x: B): any; (x: { "new"(x: RegExp): string; }): any; }
>x : any

function foo12(x: I);
Expand Down Expand Up @@ -190,16 +190,16 @@ function foo13(x: any) { }
>x : any

function foo14(x: I);
>foo14 : { (x: I): any; (x: { new(x: RegExp): string; }): any; }
>foo14 : { (x: I): any; (x: { "new"(x: RegExp): string; }): any; }
>x : I

function foo14(x: typeof b); // ok
>foo14 : { (x: I): any; (x: typeof b): any; }
>x : { new(x: RegExp): string; }
>b : { new(x: RegExp): string; }
>x : { "new"(x: RegExp): string; }
>b : { "new"(x: RegExp): string; }

function foo14(x: any) { }
>foo14 : { (x: I): any; (x: { new(x: RegExp): string; }): any; }
>foo14 : { (x: I): any; (x: { "new"(x: RegExp): string; }): any; }
>x : any

function foo15(x: I2<string>);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ var a: { new(x: string, y: string): string }
>y : string

var b = { new(x: string) { return ''; } }; // not a construct signature, function called new
>b : { new(x: string): string; }
>{ new(x: string) { return ''; } } : { new(x: string): string; }
>b : { "new"(x: string): string; }
>{ new(x: string) { return ''; } } : { "new"(x: string): string; }
>new : (x: string) => string
>x : string
>'' : ""
Expand Down Expand Up @@ -92,17 +92,17 @@ function foo3(x: any) { }
>x : any

function foo4(x: typeof b);
>foo4 : { (x: typeof b): any; (x: { new(x: string): string; }): any; }
>x : { new(x: string): string; }
>b : { new(x: string): string; }
>foo4 : { (x: typeof b): any; (x: { "new"(x: string): string; }): any; }
>x : { "new"(x: string): string; }
>b : { "new"(x: string): string; }

function foo4(x: typeof b); // error
>foo4 : { (x: { new(x: string): string; }): any; (x: typeof b): any; }
>x : { new(x: string): string; }
>b : { new(x: string): string; }
>foo4 : { (x: { "new"(x: string): string; }): any; (x: typeof b): any; }
>x : { "new"(x: string): string; }
>b : { "new"(x: string): string; }

function foo4(x: any) { }
>foo4 : { (x: { new(x: string): string; }): any; (x: { new(x: string): string; }): any; }
>foo4 : { (x: { "new"(x: string): string; }): any; (x: { "new"(x: string): string; }): any; }
>x : any

function foo8(x: B);
Expand Down Expand Up @@ -143,16 +143,16 @@ function foo10(x: any) { }
>x : any

function foo11(x: B);
>foo11 : { (x: B): any; (x: { new(x: string): string; }): any; }
>foo11 : { (x: B): any; (x: { "new"(x: string): string; }): any; }
>x : B

function foo11(x: typeof b); // ok
>foo11 : { (x: B): any; (x: typeof b): any; }
>x : { new(x: string): string; }
>b : { new(x: string): string; }
>x : { "new"(x: string): string; }
>b : { "new"(x: string): string; }

function foo11(x: any) { }
>foo11 : { (x: B): any; (x: { new(x: string): string; }): any; }
>foo11 : { (x: B): any; (x: { "new"(x: string): string; }): any; }
>x : any

function foo12(x: I);
Expand Down Expand Up @@ -193,16 +193,16 @@ function foo13(x: any) { }
>x : any

function foo14(x: I);
>foo14 : { (x: I): any; (x: { new(x: string): string; }): any; }
>foo14 : { (x: I): any; (x: { "new"(x: string): string; }): any; }
>x : I

function foo14(x: typeof b); // ok
>foo14 : { (x: I): any; (x: typeof b): any; }
>x : { new(x: string): string; }
>b : { new(x: string): string; }
>x : { "new"(x: string): string; }
>b : { "new"(x: string): string; }

function foo14(x: any) { }
>foo14 : { (x: I): any; (x: { new(x: string): string; }): any; }
>foo14 : { (x: I): any; (x: { "new"(x: string): string; }): any; }
>x : any

function foo15(x: I2<string>);
Expand Down
Loading

0 comments on commit 9cbcf01

Please sign in to comment.