diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index b8863458815ad..96a35d5a4c77d 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -163,7 +163,7 @@ namespace ts { IsFunctionExpression = 1 << 4, HasLocals = 1 << 5, IsInterface = 1 << 6, - IsObjectLiteralOrClassExpressionMethod = 1 << 7, + IsObjectLiteralOrClassExpressionMethodOrAccessor = 1 << 7, } function initFlowNode(node: T) { @@ -663,8 +663,8 @@ namespace ts { // similarly to break statements that exit to a label just past the statement body. if (!isIIFE) { currentFlow = initFlowNode({ flags: FlowFlags.Start }); - if (containerFlags & (ContainerFlags.IsFunctionExpression | ContainerFlags.IsObjectLiteralOrClassExpressionMethod)) { - currentFlow.node = node as FunctionExpression | ArrowFunction | MethodDeclaration; + if (containerFlags & (ContainerFlags.IsFunctionExpression | ContainerFlags.IsObjectLiteralOrClassExpressionMethodOrAccessor)) { + currentFlow.node = node as FunctionExpression | ArrowFunction | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration; } } // We create a return control flow graph for IIFEs and constructors. For constructors @@ -1815,16 +1815,16 @@ namespace ts { case SyntaxKind.SourceFile: return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals; + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: case SyntaxKind.MethodDeclaration: - if (isObjectLiteralOrClassExpressionMethod(node)) { - return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsObjectLiteralOrClassExpressionMethod; + if (isObjectLiteralOrClassExpressionMethodOrAccessor(node)) { + return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsObjectLiteralOrClassExpressionMethodOrAccessor; } // falls through case SyntaxKind.Constructor: case SyntaxKind.FunctionDeclaration: case SyntaxKind.MethodSignature: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: case SyntaxKind.CallSignature: case SyntaxKind.JSDocSignature: case SyntaxKind.JSDocFunctionType: @@ -3372,7 +3372,7 @@ namespace ts { emitFlags |= NodeFlags.HasAsyncFunctions; } - if (currentFlow && isObjectLiteralOrClassExpressionMethod(node)) { + if (currentFlow && isObjectLiteralOrClassExpressionMethodOrAccessor(node)) { node.flowNode = currentFlow; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 95f9450a57898..62036de75d776 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24479,7 +24479,7 @@ namespace ts { // a const variable or parameter from an outer function, we extend the origin of the control flow // analysis to include the immediately enclosing function. while (flowContainer !== declarationContainer && (flowContainer.kind === SyntaxKind.FunctionExpression || - flowContainer.kind === SyntaxKind.ArrowFunction || isObjectLiteralOrClassExpressionMethod(flowContainer)) && + flowContainer.kind === SyntaxKind.ArrowFunction || isObjectLiteralOrClassExpressionMethodOrAccessor(flowContainer)) && (isConstVariable(localOrExportSymbol) && type !== autoArrayType || isParameter && !isParameterAssigned(localOrExportSymbol))) { flowContainer = getControlFlowContainer(flowContainer); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 8af0cd411996c..1af5eefc9c85c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3410,7 +3410,7 @@ namespace ts { // function, the node property references the function (which in turn has a flowNode // property for the containing control flow). export interface FlowStart extends FlowNodeBase { - node?: FunctionExpression | ArrowFunction | MethodDeclaration; + node?: FunctionExpression | ArrowFunction | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration; } // FlowLabel represents a junction with multiple possible preceding control flows. diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 9ba15712f6630..9e695428cc150 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1526,8 +1526,8 @@ namespace ts { return node && node.kind === SyntaxKind.MethodDeclaration && node.parent.kind === SyntaxKind.ObjectLiteralExpression; } - export function isObjectLiteralOrClassExpressionMethod(node: Node): node is MethodDeclaration { - return node.kind === SyntaxKind.MethodDeclaration && + export function isObjectLiteralOrClassExpressionMethodOrAccessor(node: Node): node is MethodDeclaration { + return (node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor) && (node.parent.kind === SyntaxKind.ObjectLiteralExpression || node.parent.kind === SyntaxKind.ClassExpression); } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 14de9571dc63a..6ca4baa8cbb41 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -1925,7 +1925,7 @@ declare namespace ts { id?: number; } export interface FlowStart extends FlowNodeBase { - node?: FunctionExpression | ArrowFunction | MethodDeclaration; + node?: FunctionExpression | ArrowFunction | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration; } export interface FlowLabel extends FlowNodeBase { antecedents: FlowNode[] | undefined; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index a83b83826341f..fea14c1fcb999 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -1925,7 +1925,7 @@ declare namespace ts { id?: number; } export interface FlowStart extends FlowNodeBase { - node?: FunctionExpression | ArrowFunction | MethodDeclaration; + node?: FunctionExpression | ArrowFunction | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration; } export interface FlowLabel extends FlowNodeBase { antecedents: FlowNode[] | undefined; diff --git a/tests/baselines/reference/gettersAndSetters.errors.txt b/tests/baselines/reference/gettersAndSetters.errors.txt index a4cc4b585e2b4..034b11c892838 100644 --- a/tests/baselines/reference/gettersAndSetters.errors.txt +++ b/tests/baselines/reference/gettersAndSetters.errors.txt @@ -1,14 +1,8 @@ -tests/cases/compiler/gettersAndSetters.ts(7,16): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. -tests/cases/compiler/gettersAndSetters.ts(8,16): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. -tests/cases/compiler/gettersAndSetters.ts(10,16): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. -tests/cases/compiler/gettersAndSetters.ts(11,16): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. tests/cases/compiler/gettersAndSetters.ts(25,13): error TS2339: Property 'Baz' does not exist on type 'C'. tests/cases/compiler/gettersAndSetters.ts(26,3): error TS2339: Property 'Baz' does not exist on type 'C'. -tests/cases/compiler/gettersAndSetters.ts(29,30): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. -tests/cases/compiler/gettersAndSetters.ts(29,53): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. -==== tests/cases/compiler/gettersAndSetters.ts (8 errors) ==== +==== tests/cases/compiler/gettersAndSetters.ts (2 errors) ==== // classes class C { public fooBack = ""; @@ -16,18 +10,10 @@ tests/cases/compiler/gettersAndSetters.ts(29,53): error TS1056: Accessors are on public bazBack = ""; public get Foo() { return this.fooBack;} // ok - ~~~ -!!! error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. public set Foo(foo:string) {this.fooBack = foo;} // ok - ~~~ -!!! error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. static get Bar() {return C.barBack;} // ok - ~~~ -!!! error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. static set Bar(bar:string) {C.barBack = bar;} // ok - ~~~ -!!! error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. public get = function() {} // ok public set = function() {} // ok @@ -50,10 +36,6 @@ tests/cases/compiler/gettersAndSetters.ts(29,53): error TS1056: Accessors are on // The Foo accessors' return and param types should be contextually typed to the Foo field var o : {Foo:number;} = {get Foo() {return 0;}, set Foo(val:number){val}}; // o - ~~~ -!!! error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. - ~~~ -!!! error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. var ofg = o.Foo; o.Foo = 0; @@ -64,4 +46,14 @@ tests/cases/compiler/gettersAndSetters.ts(29,53): error TS1056: Accessors are on } var i:I1 = function (n) {return n;} + + // Repro from #45006 + const x: string | number = Math.random() < 0.5 ? "str" : 123; + if (typeof x === "string") { + let obj = { + set prop(_: any) { x.toUpperCase(); }, + get prop() { return x.toUpperCase() }, + method() { return x.toUpperCase() } + } + } \ No newline at end of file diff --git a/tests/baselines/reference/gettersAndSetters.js b/tests/baselines/reference/gettersAndSetters.js index 497bf1d3d7f60..dfcfa1c086d21 100644 --- a/tests/baselines/reference/gettersAndSetters.js +++ b/tests/baselines/reference/gettersAndSetters.js @@ -38,36 +38,33 @@ interface I1 { } var i:I1 = function (n) {return n;} + +// Repro from #45006 +const x: string | number = Math.random() < 0.5 ? "str" : 123; +if (typeof x === "string") { + let obj = { + set prop(_: any) { x.toUpperCase(); }, + get prop() { return x.toUpperCase() }, + method() { return x.toUpperCase() } + } +} //// [gettersAndSetters.js] // classes -var C = /** @class */ (function () { - function C() { +class C { + constructor() { this.fooBack = ""; this.bazBack = ""; this.get = function () { }; // ok this.set = function () { }; // ok } - Object.defineProperty(C.prototype, "Foo", { - get: function () { return this.fooBack; } // ok - , - set: function (foo) { this.fooBack = foo; } // ok - , - enumerable: false, - configurable: true - }); - Object.defineProperty(C, "Bar", { - get: function () { return C.barBack; } // ok - , - set: function (bar) { C.barBack = bar; } // ok - , - enumerable: false, - configurable: true - }); - C.barBack = ""; - return C; -}()); + get Foo() { return this.fooBack; } // ok + set Foo(foo) { this.fooBack = foo; } // ok + static get Bar() { return C.barBack; } // ok + static set Bar(bar) { C.barBack = bar; } // ok +} +C.barBack = ""; var c = new C(); var foo = c.Foo; c.Foo = "foov"; @@ -80,3 +77,12 @@ var o = { get Foo() { return 0; }, set Foo(val) { val; } }; // o var ofg = o.Foo; o.Foo = 0; var i = function (n) { return n; }; +// Repro from #45006 +const x = Math.random() < 0.5 ? "str" : 123; +if (typeof x === "string") { + let obj = { + set prop(_) { x.toUpperCase(); }, + get prop() { return x.toUpperCase(); }, + method() { return x.toUpperCase(); } + }; +} diff --git a/tests/baselines/reference/gettersAndSetters.symbols b/tests/baselines/reference/gettersAndSetters.symbols index 574272f7560ca..90cfcea315bea 100644 --- a/tests/baselines/reference/gettersAndSetters.symbols +++ b/tests/baselines/reference/gettersAndSetters.symbols @@ -114,3 +114,37 @@ var i:I1 = function (n) {return n;} >n : Symbol(n, Decl(gettersAndSetters.ts, 38, 21)) >n : Symbol(n, Decl(gettersAndSetters.ts, 38, 21)) +// Repro from #45006 +const x: string | number = Math.random() < 0.5 ? "str" : 123; +>x : Symbol(x, Decl(gettersAndSetters.ts, 41, 5)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + +if (typeof x === "string") { +>x : Symbol(x, Decl(gettersAndSetters.ts, 41, 5)) + + let obj = { +>obj : Symbol(obj, Decl(gettersAndSetters.ts, 43, 5)) + + set prop(_: any) { x.toUpperCase(); }, +>prop : Symbol(prop, Decl(gettersAndSetters.ts, 43, 13), Decl(gettersAndSetters.ts, 44, 42)) +>_ : Symbol(_, Decl(gettersAndSetters.ts, 44, 13)) +>x.toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(gettersAndSetters.ts, 41, 5)) +>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) + + get prop() { return x.toUpperCase() }, +>prop : Symbol(prop, Decl(gettersAndSetters.ts, 43, 13), Decl(gettersAndSetters.ts, 44, 42)) +>x.toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(gettersAndSetters.ts, 41, 5)) +>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) + + method() { return x.toUpperCase() } +>method : Symbol(method, Decl(gettersAndSetters.ts, 45, 42)) +>x.toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(gettersAndSetters.ts, 41, 5)) +>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) + } +} + diff --git a/tests/baselines/reference/gettersAndSetters.types b/tests/baselines/reference/gettersAndSetters.types index 2d12e61b8d6c2..83de52982c664 100644 --- a/tests/baselines/reference/gettersAndSetters.types +++ b/tests/baselines/reference/gettersAndSetters.types @@ -134,3 +134,50 @@ var i:I1 = function (n) {return n;} >n : number >n : number +// Repro from #45006 +const x: string | number = Math.random() < 0.5 ? "str" : 123; +>x : string | number +>Math.random() < 0.5 ? "str" : 123 : "str" | 123 +>Math.random() < 0.5 : boolean +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>0.5 : 0.5 +>"str" : "str" +>123 : 123 + +if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : string | number +>"string" : "string" + + let obj = { +>obj : { prop: any; method(): string; } +>{ set prop(_: any) { x.toUpperCase(); }, get prop() { return x.toUpperCase() }, method() { return x.toUpperCase() } } : { prop: any; method(): string; } + + set prop(_: any) { x.toUpperCase(); }, +>prop : any +>_ : any +>x.toUpperCase() : string +>x.toUpperCase : () => string +>x : string +>toUpperCase : () => string + + get prop() { return x.toUpperCase() }, +>prop : any +>x.toUpperCase() : string +>x.toUpperCase : () => string +>x : string +>toUpperCase : () => string + + method() { return x.toUpperCase() } +>method : () => string +>x.toUpperCase() : string +>x.toUpperCase : () => string +>x : string +>toUpperCase : () => string + } +} + diff --git a/tests/cases/compiler/gettersAndSetters.ts b/tests/cases/compiler/gettersAndSetters.ts index 9ae350b561e00..fc4fda85aebbd 100644 --- a/tests/cases/compiler/gettersAndSetters.ts +++ b/tests/cases/compiler/gettersAndSetters.ts @@ -1,3 +1,4 @@ +// @target: es2020 // classes class C { public fooBack = ""; @@ -37,3 +38,13 @@ interface I1 { } var i:I1 = function (n) {return n;} + +// Repro from #45006 +const x: string | number = Math.random() < 0.5 ? "str" : 123; +if (typeof x === "string") { + let obj = { + set prop(_: any) { x.toUpperCase(); }, + get prop() { return x.toUpperCase() }, + method() { return x.toUpperCase() } + } +}