diff --git a/internal/checker/nodecopy.go b/internal/checker/nodecopy.go index 425c444c5a1..c349a66da90 100644 --- a/internal/checker/nodecopy.go +++ b/internal/checker/nodecopy.go @@ -7,6 +7,7 @@ import ( "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/nodebuilder" "github.com/microsoft/typescript-go/internal/printer" + "github.com/microsoft/typescript-go/internal/scanner" ) func (b *NodeBuilderImpl) reuseNode(node *ast.Node) *ast.Node { @@ -21,14 +22,34 @@ func (b *NodeBuilderImpl) tryJSTypeNodeToTypeNode(node *ast.Node) *ast.Node { return b.reuseNode(node) } -// a wrapper around `reuseNode` that handles renaming `new` to `"new"` so we don't accidentally emit constructor signatures when we don't mean to +// a wrapper around `reuseNode` for property names. It handles renaming `new` to `"new"` so we don't +// accidentally emit constructor signatures when we don't mean to, and normalizes string-literal +// property names whose text is a valid identifier into identifiers, matching the behavior of +// `createPropertyNameNodeForIdentifierOrLiteral` used when constructing fresh property name nodes +// (so that reused names emit consistently regardless of whether their containing type was reused +// from source or rebuilt from a type). func (b *NodeBuilderImpl) reuseName(node *ast.Node) *ast.Node { res := b.reuseNode(node) - if res != nil && res.Kind == ast.KindIdentifier && node.AsIdentifier().Text == "new" { + if res == nil { + return res + } + if res.Kind == ast.KindIdentifier && node.AsIdentifier().Text == "new" { str := b.f.NewStringLiteral("new", ast.TokenFlagsNone) b.e.SetOriginal(str, res) return b.setTextRange(str, res) } + if res.Kind == ast.KindStringLiteral { + text := res.AsStringLiteral().Text + // Skip normalization for "new" so that reused names like `"new"(): void` on a + // method signature are not converted to an identifier (which would become a + // construct signature). This mirrors the `isMethodNamedNew` guard in + // createPropertyNameNodeForIdentifierOrLiteral. + if text != "new" && scanner.IsIdentifierText(text, core.LanguageVariantStandard) { + ident := b.newIdentifier(text, nil) + b.e.SetOriginal(ident, res) + return b.setTextRange(ident, res) + } + } return res } diff --git a/testdata/baselines/reference/compiler/declarationEmitStringNamedPropertyConsistency.js b/testdata/baselines/reference/compiler/declarationEmitStringNamedPropertyConsistency.js new file mode 100644 index 00000000000..eafa86c6e56 --- /dev/null +++ b/testdata/baselines/reference/compiler/declarationEmitStringNamedPropertyConsistency.js @@ -0,0 +1,23 @@ +//// [tests/cases/compiler/declarationEmitStringNamedPropertyConsistency.ts] //// + +//// [declarationEmitStringNamedPropertyConsistency.ts] +function createSlice(o: T) { + return o +} +export const platformSlice = createSlice({ + "query": { + "search" : "", + "user": "" + }, +}); + + + + +//// [declarationEmitStringNamedPropertyConsistency.d.ts] +export declare const platformSlice: { + query: { + search: string; + user: string; + }; +}; diff --git a/testdata/baselines/reference/compiler/declarationEmitStringNamedPropertyConsistency.symbols b/testdata/baselines/reference/compiler/declarationEmitStringNamedPropertyConsistency.symbols new file mode 100644 index 00000000000..29d2bcad196 --- /dev/null +++ b/testdata/baselines/reference/compiler/declarationEmitStringNamedPropertyConsistency.symbols @@ -0,0 +1,28 @@ +//// [tests/cases/compiler/declarationEmitStringNamedPropertyConsistency.ts] //// + +=== declarationEmitStringNamedPropertyConsistency.ts === +function createSlice(o: T) { +>createSlice : Symbol(createSlice, Decl(declarationEmitStringNamedPropertyConsistency.ts, 0, 0)) +>T : Symbol(T, Decl(declarationEmitStringNamedPropertyConsistency.ts, 0, 21)) +>o : Symbol(o, Decl(declarationEmitStringNamedPropertyConsistency.ts, 0, 24)) +>T : Symbol(T, Decl(declarationEmitStringNamedPropertyConsistency.ts, 0, 21)) + + return o +>o : Symbol(o, Decl(declarationEmitStringNamedPropertyConsistency.ts, 0, 24)) +} +export const platformSlice = createSlice({ +>platformSlice : Symbol(platformSlice, Decl(declarationEmitStringNamedPropertyConsistency.ts, 3, 12)) +>createSlice : Symbol(createSlice, Decl(declarationEmitStringNamedPropertyConsistency.ts, 0, 0)) + + "query": { +>"query" : Symbol("query", Decl(declarationEmitStringNamedPropertyConsistency.ts, 3, 42)) + + "search" : "", +>"search" : Symbol("search", Decl(declarationEmitStringNamedPropertyConsistency.ts, 4, 14)) + + "user": "" +>"user" : Symbol("user", Decl(declarationEmitStringNamedPropertyConsistency.ts, 5, 22)) + + }, +}); + diff --git a/testdata/baselines/reference/compiler/declarationEmitStringNamedPropertyConsistency.types b/testdata/baselines/reference/compiler/declarationEmitStringNamedPropertyConsistency.types new file mode 100644 index 00000000000..f8bccd5ce7c --- /dev/null +++ b/testdata/baselines/reference/compiler/declarationEmitStringNamedPropertyConsistency.types @@ -0,0 +1,31 @@ +//// [tests/cases/compiler/declarationEmitStringNamedPropertyConsistency.ts] //// + +=== declarationEmitStringNamedPropertyConsistency.ts === +function createSlice(o: T) { +>createSlice : (o: T) => T +>o : T + + return o +>o : T +} +export const platformSlice = createSlice({ +>platformSlice : { query: { search: string; user: string; }; } +>createSlice({ "query": { "search" : "", "user": "" },}) : { query: { search: string; user: string; }; } +>createSlice : (o: T) => T +>{ "query": { "search" : "", "user": "" },} : { query: { search: string; user: string; }; } + + "query": { +>"query" : { search: string; user: string; } +>{ "search" : "", "user": "" } : { search: string; user: string; } + + "search" : "", +>"search" : string +>"" : "" + + "user": "" +>"user" : string +>"" : "" + + }, +}); + diff --git a/testdata/baselines/reference/submodule/compiler/escapedReservedCompilerNamedIdentifier.js b/testdata/baselines/reference/submodule/compiler/escapedReservedCompilerNamedIdentifier.js index f0e1a76bec7..b3421b977a7 100644 --- a/testdata/baselines/reference/submodule/compiler/escapedReservedCompilerNamedIdentifier.js +++ b/testdata/baselines/reference/submodule/compiler/escapedReservedCompilerNamedIdentifier.js @@ -69,7 +69,7 @@ var b5 = o5["_proto__"]; //// [escapedReservedCompilerNamedIdentifier.d.ts] declare var __proto__: number; declare var o: { - "__proto__": number; + __proto__: number; }; declare var b: number; declare var o1: { @@ -78,7 +78,7 @@ declare var o1: { declare var b1: number; declare var ___proto__: number; declare var o2: { - "___proto__": number; + ___proto__: number; }; declare var b2: number; declare var o3: { @@ -87,7 +87,7 @@ declare var o3: { declare var b3: number; declare var _proto__: number; declare var o4: { - "_proto__": number; + _proto__: number; }; declare var b4: number; declare var o5: { diff --git a/testdata/baselines/reference/submodule/conformance/jsDeclarationsPackageJson(target=es2015).types b/testdata/baselines/reference/submodule/conformance/jsDeclarationsPackageJson(target=es2015).types index 143360a6b5d..23590f0399b 100644 --- a/testdata/baselines/reference/submodule/conformance/jsDeclarationsPackageJson(target=es2015).types +++ b/testdata/baselines/reference/submodule/conformance/jsDeclarationsPackageJson(target=es2015).types @@ -2,17 +2,17 @@ === index.js === const j = require("./package.json"); ->j : { name: string; version: string; description: string; main: string; bin: { "cli": string; }; engines: { "node": string; }; scripts: { "scriptname": string; }; devDependencies: { "@ns/dep": string; }; dependencies: { "dep": string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { "o": string[]; }; } ->require("./package.json") : { name: string; version: string; description: string; main: string; bin: { "cli": string; }; engines: { "node": string; }; scripts: { "scriptname": string; }; devDependencies: { "@ns/dep": string; }; dependencies: { "dep": string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { "o": string[]; }; } +>j : { name: string; version: string; description: string; main: string; bin: { cli: string; }; engines: { node: string; }; scripts: { scriptname: string; }; devDependencies: { "@ns/dep": string; }; dependencies: { dep: string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { o: string[]; }; } +>require("./package.json") : { name: string; version: string; description: string; main: string; bin: { cli: string; }; engines: { node: string; }; scripts: { scriptname: string; }; devDependencies: { "@ns/dep": string; }; dependencies: { dep: string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { o: string[]; }; } >require : any >"./package.json" : "./package.json" module.exports = j; ->module.exports = j : { name: string; version: string; description: string; main: string; bin: { "cli": string; }; engines: { "node": string; }; scripts: { "scriptname": string; }; devDependencies: { "@ns/dep": string; }; dependencies: { "dep": string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { "o": string[]; }; } ->module.exports : { name: string; version: string; description: string; main: string; bin: { "cli": string; }; engines: { "node": string; }; scripts: { "scriptname": string; }; devDependencies: { "@ns/dep": string; }; dependencies: { "dep": string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { "o": string[]; }; } ->module : { exports: { name: string; version: string; description: string; main: string; bin: { "cli": string; }; engines: { "node": string; }; scripts: { "scriptname": string; }; devDependencies: { "@ns/dep": string; }; dependencies: { "dep": string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { "o": string[]; }; }; } ->exports : { name: string; version: string; description: string; main: string; bin: { "cli": string; }; engines: { "node": string; }; scripts: { "scriptname": string; }; devDependencies: { "@ns/dep": string; }; dependencies: { "dep": string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { "o": string[]; }; } ->j : { name: string; version: string; description: string; main: string; bin: { "cli": string; }; engines: { "node": string; }; scripts: { "scriptname": string; }; devDependencies: { "@ns/dep": string; }; dependencies: { "dep": string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { "o": string[]; }; } +>module.exports = j : { name: string; version: string; description: string; main: string; bin: { cli: string; }; engines: { node: string; }; scripts: { scriptname: string; }; devDependencies: { "@ns/dep": string; }; dependencies: { dep: string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { o: string[]; }; } +>module.exports : { name: string; version: string; description: string; main: string; bin: { cli: string; }; engines: { node: string; }; scripts: { scriptname: string; }; devDependencies: { "@ns/dep": string; }; dependencies: { dep: string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { o: string[]; }; } +>module : { exports: { name: string; version: string; description: string; main: string; bin: { cli: string; }; engines: { node: string; }; scripts: { scriptname: string; }; devDependencies: { "@ns/dep": string; }; dependencies: { dep: string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { o: string[]; }; }; } +>exports : { name: string; version: string; description: string; main: string; bin: { cli: string; }; engines: { node: string; }; scripts: { scriptname: string; }; devDependencies: { "@ns/dep": string; }; dependencies: { dep: string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { o: string[]; }; } +>j : { name: string; version: string; description: string; main: string; bin: { cli: string; }; engines: { node: string; }; scripts: { scriptname: string; }; devDependencies: { "@ns/dep": string; }; dependencies: { dep: string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { o: string[]; }; } === package.json === { diff --git a/testdata/baselines/reference/submodule/conformance/jsDeclarationsPackageJson(target=es2015).types.diff b/testdata/baselines/reference/submodule/conformance/jsDeclarationsPackageJson(target=es2015).types.diff deleted file mode 100644 index 535b4c3a9e3..00000000000 --- a/testdata/baselines/reference/submodule/conformance/jsDeclarationsPackageJson(target=es2015).types.diff +++ /dev/null @@ -1,27 +0,0 @@ ---- old.jsDeclarationsPackageJson(target=es2015).types -+++ new.jsDeclarationsPackageJson(target=es2015).types -@@= skipped -1, +1 lines =@@ - - === index.js === - const j = require("./package.json"); -->j : { name: string; version: string; description: string; main: string; bin: { cli: string; }; engines: { node: string; }; scripts: { scriptname: string; }; devDependencies: { "@ns/dep": string; }; dependencies: { dep: string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { o: string[]; }; } -->require("./package.json") : { name: string; version: string; description: string; main: string; bin: { cli: string; }; engines: { node: string; }; scripts: { scriptname: string; }; devDependencies: { "@ns/dep": string; }; dependencies: { dep: string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { o: string[]; }; } -+>j : { name: string; version: string; description: string; main: string; bin: { "cli": string; }; engines: { "node": string; }; scripts: { "scriptname": string; }; devDependencies: { "@ns/dep": string; }; dependencies: { "dep": string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { "o": string[]; }; } -+>require("./package.json") : { name: string; version: string; description: string; main: string; bin: { "cli": string; }; engines: { "node": string; }; scripts: { "scriptname": string; }; devDependencies: { "@ns/dep": string; }; dependencies: { "dep": string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { "o": string[]; }; } - >require : any - >"./package.json" : "./package.json" - - module.exports = j; -->module.exports = j : { name: string; version: string; description: string; main: string; bin: { cli: string; }; engines: { node: string; }; scripts: { scriptname: string; }; devDependencies: { "@ns/dep": string; }; dependencies: { dep: string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { o: string[]; }; } -->module.exports : { name: string; version: string; description: string; main: string; bin: { cli: string; }; engines: { node: string; }; scripts: { scriptname: string; }; devDependencies: { "@ns/dep": string; }; dependencies: { dep: string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { o: string[]; }; } -->module : { exports: { name: string; version: string; description: string; main: string; bin: { cli: string; }; engines: { node: string; }; scripts: { scriptname: string; }; devDependencies: { "@ns/dep": string; }; dependencies: { dep: string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { o: string[]; }; }; } -->exports : { name: string; version: string; description: string; main: string; bin: { cli: string; }; engines: { node: string; }; scripts: { scriptname: string; }; devDependencies: { "@ns/dep": string; }; dependencies: { dep: string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { o: string[]; }; } -->j : { name: string; version: string; description: string; main: string; bin: { cli: string; }; engines: { node: string; }; scripts: { scriptname: string; }; devDependencies: { "@ns/dep": string; }; dependencies: { dep: string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { o: string[]; }; } -+>module.exports = j : { name: string; version: string; description: string; main: string; bin: { "cli": string; }; engines: { "node": string; }; scripts: { "scriptname": string; }; devDependencies: { "@ns/dep": string; }; dependencies: { "dep": string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { "o": string[]; }; } -+>module.exports : { name: string; version: string; description: string; main: string; bin: { "cli": string; }; engines: { "node": string; }; scripts: { "scriptname": string; }; devDependencies: { "@ns/dep": string; }; dependencies: { "dep": string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { "o": string[]; }; } -+>module : { exports: { name: string; version: string; description: string; main: string; bin: { "cli": string; }; engines: { "node": string; }; scripts: { "scriptname": string; }; devDependencies: { "@ns/dep": string; }; dependencies: { "dep": string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { "o": string[]; }; }; } -+>exports : { name: string; version: string; description: string; main: string; bin: { "cli": string; }; engines: { "node": string; }; scripts: { "scriptname": string; }; devDependencies: { "@ns/dep": string; }; dependencies: { "dep": string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { "o": string[]; }; } -+>j : { name: string; version: string; description: string; main: string; bin: { "cli": string; }; engines: { "node": string; }; scripts: { "scriptname": string; }; devDependencies: { "@ns/dep": string; }; dependencies: { "dep": string; }; repository: string; keywords: string[]; author: string; license: string; homepage: string; config: { "o": string[]; }; } - - === package.json === - { \ No newline at end of file diff --git a/testdata/baselines/reference/submoduleAccepted/compiler/escapedReservedCompilerNamedIdentifier.js.diff b/testdata/baselines/reference/submoduleAccepted/compiler/escapedReservedCompilerNamedIdentifier.js.diff deleted file mode 100644 index 09817ace6ee..00000000000 --- a/testdata/baselines/reference/submoduleAccepted/compiler/escapedReservedCompilerNamedIdentifier.js.diff +++ /dev/null @@ -1,29 +0,0 @@ ---- old.escapedReservedCompilerNamedIdentifier.js -+++ new.escapedReservedCompilerNamedIdentifier.js -@@= skipped -68, +68 lines =@@ - //// [escapedReservedCompilerNamedIdentifier.d.ts] - declare var __proto__: number; - declare var o: { -- __proto__: number; -+ "__proto__": number; - }; - declare var b: number; - declare var o1: { -@@= skipped -9, +9 lines =@@ - declare var b1: number; - declare var ___proto__: number; - declare var o2: { -- ___proto__: number; -+ "___proto__": number; - }; - declare var b2: number; - declare var o3: { -@@= skipped -9, +9 lines =@@ - declare var b3: number; - declare var _proto__: number; - declare var o4: { -- _proto__: number; -+ "_proto__": number; - }; - declare var b4: number; - declare var o5: { \ No newline at end of file diff --git a/testdata/submoduleAccepted.txt b/testdata/submoduleAccepted.txt index f4ce0423807..2e766b284a8 100644 --- a/testdata/submoduleAccepted.txt +++ b/testdata/submoduleAccepted.txt @@ -1248,9 +1248,6 @@ compiler/defaultDeclarationEmitShadowedNamedCorrectly.js.diff compiler/es5ExportEqualsDts(target=es2015).js.diff compiler/declarationEmitNameConflicts.js.diff -## Properties with __proto__-like names now quoted in declaration emit (it's quoted in the originating sourcefile) -compiler/escapedReservedCompilerNamedIdentifier.js.diff - ## Nested /*elided*/ type annotation comments removed at deeper nesting levels (`/*elided*/` comments are internal artifacts with no semantic meaning) ## Declaration emit no longer preserves elided comments in nested class expressions compiler/emitClassExpressionInDeclarationFile.js.diff diff --git a/testdata/tests/cases/compiler/declarationEmitStringNamedPropertyConsistency.ts b/testdata/tests/cases/compiler/declarationEmitStringNamedPropertyConsistency.ts new file mode 100644 index 00000000000..62d5c7bd875 --- /dev/null +++ b/testdata/tests/cases/compiler/declarationEmitStringNamedPropertyConsistency.ts @@ -0,0 +1,12 @@ +// @declaration: true +// @emitDeclarationOnly: true + +function createSlice(o: T) { + return o +} +export const platformSlice = createSlice({ + "query": { + "search" : "", + "user": "" + }, +});