Skip to content

Commit 9cbcf01

Browse files
jakebaileyAndarist
andauthored
Ensure generated property names for methods named "new" are quoted (#55750)
Co-authored-by: Mateusz Burzyński <[email protected]>
1 parent b3770e7 commit 9cbcf01

22 files changed

+365
-218
lines changed

src/compiler/checker.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8213,16 +8213,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
82138213
function getPropertyNameNodeForSymbol(symbol: Symbol, context: NodeBuilderContext) {
82148214
const stringNamed = !!length(symbol.declarations) && every(symbol.declarations, isStringNamed);
82158215
const singleQuote = !!length(symbol.declarations) && every(symbol.declarations, isSingleQuotedStringNamed);
8216-
const fromNameType = getPropertyNameNodeForSymbolFromNameType(symbol, context, singleQuote, stringNamed);
8216+
const isMethod = !!(symbol.flags & SymbolFlags.Method);
8217+
const fromNameType = getPropertyNameNodeForSymbolFromNameType(symbol, context, singleQuote, stringNamed, isMethod);
82178218
if (fromNameType) {
82188219
return fromNameType;
82198220
}
82208221
const rawName = unescapeLeadingUnderscores(symbol.escapedName);
8221-
return createPropertyNameNodeForIdentifierOrLiteral(rawName, getEmitScriptTarget(compilerOptions), singleQuote, stringNamed);
8222+
return createPropertyNameNodeForIdentifierOrLiteral(rawName, getEmitScriptTarget(compilerOptions), singleQuote, stringNamed, isMethod);
82228223
}
82238224

82248225
// See getNameForSymbolFromNameType for a stringy equivalent
8225-
function getPropertyNameNodeForSymbolFromNameType(symbol: Symbol, context: NodeBuilderContext, singleQuote?: boolean, stringNamed?: boolean) {
8226+
function getPropertyNameNodeForSymbolFromNameType(symbol: Symbol, context: NodeBuilderContext, singleQuote: boolean, stringNamed: boolean, isMethod: boolean) {
82268227
const nameType = getSymbolLinks(symbol).nameType;
82278228
if (nameType) {
82288229
if (nameType.flags & TypeFlags.StringOrNumberLiteral) {
@@ -8233,7 +8234,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
82338234
if (isNumericLiteralName(name) && startsWith(name, "-")) {
82348235
return factory.createComputedPropertyName(factory.createPrefixUnaryExpression(SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(+name))));
82358236
}
8236-
return createPropertyNameNodeForIdentifierOrLiteral(name, getEmitScriptTarget(compilerOptions));
8237+
return createPropertyNameNodeForIdentifierOrLiteral(name, getEmitScriptTarget(compilerOptions), singleQuote, stringNamed, isMethod);
82378238
}
82388239
if (nameType.flags & TypeFlags.UniqueESSymbol) {
82398240
return factory.createComputedPropertyName(symbolToExpression((nameType as UniqueESSymbolType).symbol, context, SymbolFlags.Value));

src/compiler/utilities.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10216,9 +10216,10 @@ export function isNumericLiteralName(name: string | __String) {
1021610216
}
1021710217

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

src/services/codefixes/fixAddMissingMember.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,8 @@ function createPropertyNameFromSymbol(symbol: Symbol, target: ScriptTarget, quot
772772
const prop = checker.symbolToNode(symbol, SymbolFlags.Value, /*enclosingDeclaration*/ undefined, NodeBuilderFlags.WriteComputedProps);
773773
if (prop && isComputedPropertyName(prop)) return prop;
774774
}
775-
return createPropertyNameNodeForIdentifierOrLiteral(symbol.name, target, quotePreference === QuotePreference.Single);
775+
// We're using these nodes as property names in an object literal; no need to quote names when not needed.
776+
return createPropertyNameNodeForIdentifierOrLiteral(symbol.name, target, quotePreference === QuotePreference.Single, /*stringNamed*/ false, /*isMethod*/ false);
776777
}
777778

778779
function findScope(node: Node) {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//// [tests/cases/compiler/emitMethodCalledNew.ts] ////
2+
3+
//// [emitMethodCalledNew.ts]
4+
// https://github.com/microsoft/TypeScript/issues/55075
5+
6+
export const a = {
7+
new(x: number) { return x + 1 }
8+
}
9+
export const b = {
10+
"new"(x: number) { return x + 1 }
11+
}
12+
export const c = {
13+
["new"](x: number) { return x + 1 }
14+
}
15+
16+
17+
//// [emitMethodCalledNew.js]
18+
"use strict";
19+
// https://github.com/microsoft/TypeScript/issues/55075
20+
var _a;
21+
Object.defineProperty(exports, "__esModule", { value: true });
22+
exports.c = exports.b = exports.a = void 0;
23+
exports.a = {
24+
new: function (x) { return x + 1; }
25+
};
26+
exports.b = {
27+
"new": function (x) { return x + 1; }
28+
};
29+
exports.c = (_a = {},
30+
_a["new"] = function (x) { return x + 1; },
31+
_a);
32+
33+
34+
//// [emitMethodCalledNew.d.ts]
35+
export declare const a: {
36+
"new"(x: number): number;
37+
};
38+
export declare const b: {
39+
"new"(x: number): number;
40+
};
41+
export declare const c: {
42+
"new"(x: number): number;
43+
};
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//// [tests/cases/compiler/emitMethodCalledNew.ts] ////
2+
3+
=== emitMethodCalledNew.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/55075
5+
6+
export const a = {
7+
>a : Symbol(a, Decl(emitMethodCalledNew.ts, 2, 12))
8+
9+
new(x: number) { return x + 1 }
10+
>new : Symbol(new, Decl(emitMethodCalledNew.ts, 2, 18))
11+
>x : Symbol(x, Decl(emitMethodCalledNew.ts, 3, 6))
12+
>x : Symbol(x, Decl(emitMethodCalledNew.ts, 3, 6))
13+
}
14+
export const b = {
15+
>b : Symbol(b, Decl(emitMethodCalledNew.ts, 5, 12))
16+
17+
"new"(x: number) { return x + 1 }
18+
>"new" : Symbol("new", Decl(emitMethodCalledNew.ts, 5, 18))
19+
>x : Symbol(x, Decl(emitMethodCalledNew.ts, 6, 8))
20+
>x : Symbol(x, Decl(emitMethodCalledNew.ts, 6, 8))
21+
}
22+
export const c = {
23+
>c : Symbol(c, Decl(emitMethodCalledNew.ts, 8, 12))
24+
25+
["new"](x: number) { return x + 1 }
26+
>["new"] : Symbol(["new"], Decl(emitMethodCalledNew.ts, 8, 18))
27+
>"new" : Symbol(["new"], Decl(emitMethodCalledNew.ts, 8, 18))
28+
>x : Symbol(x, Decl(emitMethodCalledNew.ts, 9, 10))
29+
>x : Symbol(x, Decl(emitMethodCalledNew.ts, 9, 10))
30+
}
31+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//// [tests/cases/compiler/emitMethodCalledNew.ts] ////
2+
3+
=== emitMethodCalledNew.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/55075
5+
6+
export const a = {
7+
>a : { "new"(x: number): number; }
8+
>{ new(x: number) { return x + 1 }} : { "new"(x: number): number; }
9+
10+
new(x: number) { return x + 1 }
11+
>new : (x: number) => number
12+
>x : number
13+
>x + 1 : number
14+
>x : number
15+
>1 : 1
16+
}
17+
export const b = {
18+
>b : { "new"(x: number): number; }
19+
>{ "new"(x: number) { return x + 1 }} : { "new"(x: number): number; }
20+
21+
"new"(x: number) { return x + 1 }
22+
>"new" : (x: number) => number
23+
>x : number
24+
>x + 1 : number
25+
>x : number
26+
>1 : 1
27+
}
28+
export const c = {
29+
>c : { "new"(x: number): number; }
30+
>{ ["new"](x: number) { return x + 1 }} : { "new"(x: number): number; }
31+
32+
["new"](x: number) { return x + 1 }
33+
>["new"] : (x: number) => number
34+
>"new" : "new"
35+
>x : number
36+
>x + 1 : number
37+
>x : number
38+
>1 : 1
39+
}
40+

tests/baselines/reference/objectTypesIdentityWithConstructSignatures2.types

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ var a: { new(x: Date): string }
3232
>x : Date
3333

3434
var b = { new(x: RegExp) { return ''; } }; // not a construct signature, function called new
35-
>b : { new(x: RegExp): string; }
36-
>{ new(x: RegExp) { return ''; } } : { new(x: RegExp): string; }
35+
>b : { "new"(x: RegExp): string; }
36+
>{ new(x: RegExp) { return ''; } } : { "new"(x: RegExp): string; }
3737
>new : (x: RegExp) => string
3838
>x : RegExp
3939
>'' : ""
@@ -89,17 +89,17 @@ function foo3(x: any) { }
8989
>x : any
9090

9191
function foo4(x: typeof b);
92-
>foo4 : { (x: typeof b): any; (x: { new(x: RegExp): string; }): any; }
93-
>x : { new(x: RegExp): string; }
94-
>b : { new(x: RegExp): string; }
92+
>foo4 : { (x: typeof b): any; (x: { "new"(x: RegExp): string; }): any; }
93+
>x : { "new"(x: RegExp): string; }
94+
>b : { "new"(x: RegExp): string; }
9595

9696
function foo4(x: typeof b); // error
97-
>foo4 : { (x: { new(x: RegExp): string; }): any; (x: typeof b): any; }
98-
>x : { new(x: RegExp): string; }
99-
>b : { new(x: RegExp): string; }
97+
>foo4 : { (x: { "new"(x: RegExp): string; }): any; (x: typeof b): any; }
98+
>x : { "new"(x: RegExp): string; }
99+
>b : { "new"(x: RegExp): string; }
100100

101101
function foo4(x: any) { }
102-
>foo4 : { (x: { new(x: RegExp): string; }): any; (x: { new(x: RegExp): string; }): any; }
102+
>foo4 : { (x: { "new"(x: RegExp): string; }): any; (x: { "new"(x: RegExp): string; }): any; }
103103
>x : any
104104

105105
function foo8(x: B);
@@ -140,16 +140,16 @@ function foo10(x: any) { }
140140
>x : any
141141

142142
function foo11(x: B);
143-
>foo11 : { (x: B): any; (x: { new(x: RegExp): string; }): any; }
143+
>foo11 : { (x: B): any; (x: { "new"(x: RegExp): string; }): any; }
144144
>x : B
145145

146146
function foo11(x: typeof b); // ok
147147
>foo11 : { (x: B): any; (x: typeof b): any; }
148-
>x : { new(x: RegExp): string; }
149-
>b : { new(x: RegExp): string; }
148+
>x : { "new"(x: RegExp): string; }
149+
>b : { "new"(x: RegExp): string; }
150150

151151
function foo11(x: any) { }
152-
>foo11 : { (x: B): any; (x: { new(x: RegExp): string; }): any; }
152+
>foo11 : { (x: B): any; (x: { "new"(x: RegExp): string; }): any; }
153153
>x : any
154154

155155
function foo12(x: I);
@@ -190,16 +190,16 @@ function foo13(x: any) { }
190190
>x : any
191191

192192
function foo14(x: I);
193-
>foo14 : { (x: I): any; (x: { new(x: RegExp): string; }): any; }
193+
>foo14 : { (x: I): any; (x: { "new"(x: RegExp): string; }): any; }
194194
>x : I
195195

196196
function foo14(x: typeof b); // ok
197197
>foo14 : { (x: I): any; (x: typeof b): any; }
198-
>x : { new(x: RegExp): string; }
199-
>b : { new(x: RegExp): string; }
198+
>x : { "new"(x: RegExp): string; }
199+
>b : { "new"(x: RegExp): string; }
200200

201201
function foo14(x: any) { }
202-
>foo14 : { (x: I): any; (x: { new(x: RegExp): string; }): any; }
202+
>foo14 : { (x: I): any; (x: { "new"(x: RegExp): string; }): any; }
203203
>x : any
204204

205205
function foo15(x: I2<string>);

tests/baselines/reference/objectTypesIdentityWithConstructSignaturesDifferingParamCounts.types

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ var a: { new(x: string, y: string): string }
3535
>y : string
3636

3737
var b = { new(x: string) { return ''; } }; // not a construct signature, function called new
38-
>b : { new(x: string): string; }
39-
>{ new(x: string) { return ''; } } : { new(x: string): string; }
38+
>b : { "new"(x: string): string; }
39+
>{ new(x: string) { return ''; } } : { "new"(x: string): string; }
4040
>new : (x: string) => string
4141
>x : string
4242
>'' : ""
@@ -92,17 +92,17 @@ function foo3(x: any) { }
9292
>x : any
9393

9494
function foo4(x: typeof b);
95-
>foo4 : { (x: typeof b): any; (x: { new(x: string): string; }): any; }
96-
>x : { new(x: string): string; }
97-
>b : { new(x: string): string; }
95+
>foo4 : { (x: typeof b): any; (x: { "new"(x: string): string; }): any; }
96+
>x : { "new"(x: string): string; }
97+
>b : { "new"(x: string): string; }
9898

9999
function foo4(x: typeof b); // error
100-
>foo4 : { (x: { new(x: string): string; }): any; (x: typeof b): any; }
101-
>x : { new(x: string): string; }
102-
>b : { new(x: string): string; }
100+
>foo4 : { (x: { "new"(x: string): string; }): any; (x: typeof b): any; }
101+
>x : { "new"(x: string): string; }
102+
>b : { "new"(x: string): string; }
103103

104104
function foo4(x: any) { }
105-
>foo4 : { (x: { new(x: string): string; }): any; (x: { new(x: string): string; }): any; }
105+
>foo4 : { (x: { "new"(x: string): string; }): any; (x: { "new"(x: string): string; }): any; }
106106
>x : any
107107

108108
function foo8(x: B);
@@ -143,16 +143,16 @@ function foo10(x: any) { }
143143
>x : any
144144

145145
function foo11(x: B);
146-
>foo11 : { (x: B): any; (x: { new(x: string): string; }): any; }
146+
>foo11 : { (x: B): any; (x: { "new"(x: string): string; }): any; }
147147
>x : B
148148

149149
function foo11(x: typeof b); // ok
150150
>foo11 : { (x: B): any; (x: typeof b): any; }
151-
>x : { new(x: string): string; }
152-
>b : { new(x: string): string; }
151+
>x : { "new"(x: string): string; }
152+
>b : { "new"(x: string): string; }
153153

154154
function foo11(x: any) { }
155-
>foo11 : { (x: B): any; (x: { new(x: string): string; }): any; }
155+
>foo11 : { (x: B): any; (x: { "new"(x: string): string; }): any; }
156156
>x : any
157157

158158
function foo12(x: I);
@@ -193,16 +193,16 @@ function foo13(x: any) { }
193193
>x : any
194194

195195
function foo14(x: I);
196-
>foo14 : { (x: I): any; (x: { new(x: string): string; }): any; }
196+
>foo14 : { (x: I): any; (x: { "new"(x: string): string; }): any; }
197197
>x : I
198198

199199
function foo14(x: typeof b); // ok
200200
>foo14 : { (x: I): any; (x: typeof b): any; }
201-
>x : { new(x: string): string; }
202-
>b : { new(x: string): string; }
201+
>x : { "new"(x: string): string; }
202+
>b : { "new"(x: string): string; }
203203

204204
function foo14(x: any) { }
205-
>foo14 : { (x: I): any; (x: { new(x: string): string; }): any; }
205+
>foo14 : { (x: I): any; (x: { "new"(x: string): string; }): any; }
206206
>x : any
207207

208208
function foo15(x: I2<string>);

0 commit comments

Comments
 (0)