Skip to content

Commit 338f3c0

Browse files
committed
Don't merge late bound assignment members with pre-existing symbols
1 parent 56a0825 commit 338f3c0

17 files changed

Lines changed: 493 additions & 17 deletions

src/compiler/checker.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13197,10 +13197,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1319713197
else if (!member.symbol.isReplaceableByMethod) {
1319813198
symbol.declarations.push(member);
1319913199
}
13200-
if (symbolFlags & SymbolFlags.Value) {
13201-
if (!symbol.valueDeclaration || symbol.valueDeclaration.kind !== member.kind) {
13202-
symbol.valueDeclaration = member;
13203-
}
13200+
if (symbolFlags & SymbolFlags.Value && !symbol.valueDeclaration) {
13201+
symbol.valueDeclaration = member;
1320413202
}
1320513203
}
1320613204

@@ -13232,7 +13230,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1323213230
* @param lateSymbols The late-bound symbols of the parent.
1323313231
* @param decl The member to bind.
1323413232
*/
13235-
function lateBindMember(parent: Symbol, earlySymbols: SymbolTable | undefined, lateSymbols: Map<__String, TransientSymbol>, decl: LateBoundDeclaration | LateBoundBinaryExpressionDeclaration) {
13233+
function lateBindMember(parent: Symbol, earlySymbols: SymbolTable | undefined, lateSymbols: Map<__String, TransientSymbol>, decl: LateBoundDeclaration | LateBoundBinaryExpressionDeclaration, isFromAssignmentDeclarationMember = false) {
1323613234
Debug.assert(!!decl.symbol, "The member is expected to have a symbol.");
1323713235
const links = getNodeLinks(decl);
1323813236
if (!links.resolvedSymbol) {
@@ -13247,10 +13245,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1324713245

1324813246
// Get or add a late-bound symbol for the member. This allows us to merge late-bound accessor declarations.
1324913247
let lateSymbol = lateSymbols.get(memberName);
13248+
if (isFromAssignmentDeclarationMember && lateSymbol) {
13249+
return links.resolvedSymbol = lateSymbol;
13250+
}
1325013251
if (!lateSymbol) lateSymbols.set(memberName, lateSymbol = createSymbol(SymbolFlags.None, memberName, CheckFlags.Late));
1325113252

1325213253
// Report an error if there's a symbol declaration with the same name and conflicting flags.
13253-
const earlySymbol = earlySymbols && earlySymbols.get(memberName);
13254+
const earlySymbol = earlySymbols?.get(memberName);
1325413255
// Duplicate property declarations of classes are checked in checkClassForDuplicateDeclarations.
1325513256
if (!(parent.flags & SymbolFlags.Class) && lateSymbol.flags & getExcludedSymbolFlags(symbolFlags)) {
1325613257
// If we have an existing early-bound member, combine its declarations so that we can
@@ -13342,7 +13343,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1334213343
|| assignmentKind === AssignmentDeclarationKind.Prototype; // A straight `Prototype` assignment probably can never have a computed name
1334313344
if (isStatic === !isInstanceMember) {
1334413345
if (hasLateBindableName(member)) {
13345-
lateBindMember(symbol, earlySymbols, lateSymbols, member);
13346+
lateBindMember(symbol, earlySymbols, lateSymbols, member, /*isFromAssignmentDeclarationMember*/ true);
1334613347
}
1334713348
}
1334813349
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//// [tests/cases/compiler/lateBoundAssignmentCandidateJS1.ts] ////
2+
3+
//// [index.js]
4+
// https://github.com/microsoft/TypeScript/issues/60590
5+
6+
export const kBar = Symbol("bar");
7+
8+
export class foo0 {
9+
/**
10+
* @protected
11+
* @type {null | string}
12+
*/
13+
[kBar] = null;
14+
15+
get bar() {
16+
return this[kBar];
17+
}
18+
/**
19+
* @type {string}
20+
*/
21+
set bar(value) {
22+
this[kBar] = value;
23+
}
24+
}
25+
26+
27+
//// [index.js]
28+
// https://github.com/microsoft/TypeScript/issues/60590
29+
export const kBar = Symbol("bar");
30+
export class foo0 {
31+
/**
32+
* @protected
33+
* @type {null | string}
34+
*/
35+
[kBar] = null;
36+
get bar() {
37+
return this[kBar];
38+
}
39+
/**
40+
* @type {string}
41+
*/
42+
set bar(value) {
43+
this[kBar] = value;
44+
}
45+
}
46+
47+
48+
//// [index.d.ts]
49+
export const kBar: unique symbol;
50+
export class foo0 {
51+
/**
52+
* @type {string}
53+
*/
54+
set bar(value: string | null);
55+
get bar(): string | null;
56+
/**
57+
* @protected
58+
* @type {null | string}
59+
*/
60+
protected [kBar]: null | string;
61+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//// [tests/cases/compiler/lateBoundAssignmentCandidateJS1.ts] ////
2+
3+
=== index.js ===
4+
// https://github.com/microsoft/TypeScript/issues/60590
5+
6+
export const kBar = Symbol("bar");
7+
>kBar : Symbol(kBar, Decl(index.js, 2, 12))
8+
>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2019.symbol.d.ts, --, --))
9+
10+
export class foo0 {
11+
>foo0 : Symbol(foo0, Decl(index.js, 2, 34))
12+
13+
/**
14+
* @protected
15+
* @type {null | string}
16+
*/
17+
[kBar] = null;
18+
>[kBar] : Symbol(foo0[kBar], Decl(index.js, 4, 19))
19+
>kBar : Symbol(kBar, Decl(index.js, 2, 12))
20+
21+
get bar() {
22+
>bar : Symbol(foo0.bar, Decl(index.js, 9, 18), Decl(index.js, 13, 5))
23+
24+
return this[kBar];
25+
>this : Symbol(foo0, Decl(index.js, 2, 34))
26+
>kBar : Symbol(kBar, Decl(index.js, 2, 12))
27+
}
28+
/**
29+
* @type {string}
30+
*/
31+
set bar(value) {
32+
>bar : Symbol(foo0.bar, Decl(index.js, 9, 18), Decl(index.js, 13, 5))
33+
>value : Symbol(value, Decl(index.js, 17, 12))
34+
35+
this[kBar] = value;
36+
>this : Symbol(foo0, Decl(index.js, 2, 34))
37+
>kBar : Symbol(kBar, Decl(index.js, 2, 12))
38+
>value : Symbol(value, Decl(index.js, 17, 12))
39+
}
40+
}
41+
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//// [tests/cases/compiler/lateBoundAssignmentCandidateJS1.ts] ////
2+
3+
=== index.js ===
4+
// https://github.com/microsoft/TypeScript/issues/60590
5+
6+
export const kBar = Symbol("bar");
7+
>kBar : unique symbol
8+
> : ^^^^^^^^^^^^^
9+
>Symbol("bar") : unique symbol
10+
> : ^^^^^^^^^^^^^
11+
>Symbol : SymbolConstructor
12+
> : ^^^^^^^^^^^^^^^^^
13+
>"bar" : "bar"
14+
> : ^^^^^
15+
16+
export class foo0 {
17+
>foo0 : foo0
18+
> : ^^^^
19+
20+
/**
21+
* @protected
22+
* @type {null | string}
23+
*/
24+
[kBar] = null;
25+
>[kBar] : string | null
26+
> : ^^^^^^^^^^^^^
27+
>kBar : unique symbol
28+
> : ^^^^^^^^^^^^^
29+
30+
get bar() {
31+
>bar : string | null
32+
> : ^^^^^^^^^^^^^
33+
34+
return this[kBar];
35+
>this[kBar] : string | null
36+
> : ^^^^^^^^^^^^^
37+
>this : this
38+
> : ^^^^
39+
>kBar : unique symbol
40+
> : ^^^^^^^^^^^^^
41+
}
42+
/**
43+
* @type {string}
44+
*/
45+
set bar(value) {
46+
>bar : string | null
47+
> : ^^^^^^^^^^^^^
48+
>value : string | null
49+
> : ^^^^^^^^^^^^^
50+
51+
this[kBar] = value;
52+
>this[kBar] = value : string | null
53+
> : ^^^^^^^^^^^^^
54+
>this[kBar] : string | null
55+
> : ^^^^^^^^^^^^^
56+
>this : this
57+
> : ^^^^
58+
>kBar : unique symbol
59+
> : ^^^^^^^^^^^^^
60+
>value : string | null
61+
> : ^^^^^^^^^^^^^
62+
}
63+
}
64+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//// [tests/cases/compiler/lateBoundAssignmentCandidateJS2.ts] ////
2+
3+
//// [index.js]
4+
const prop = 'prop';
5+
6+
export class foo1 {
7+
constructor() {
8+
this[prop] = 'bar'
9+
}
10+
11+
/**
12+
* @protected
13+
* @type {string}
14+
*/
15+
[prop] = 'baz';
16+
}
17+
18+
19+
//// [index.js]
20+
const prop = 'prop';
21+
export class foo1 {
22+
constructor() {
23+
this[prop] = 'bar';
24+
}
25+
/**
26+
* @protected
27+
* @type {string}
28+
*/
29+
[prop] = 'baz';
30+
}
31+
32+
33+
//// [index.d.ts]
34+
export class foo1 {
35+
/**
36+
* @protected
37+
* @type {string}
38+
*/
39+
protected prop: string;
40+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//// [tests/cases/compiler/lateBoundAssignmentCandidateJS2.ts] ////
2+
3+
=== index.js ===
4+
const prop = 'prop';
5+
>prop : Symbol(prop, Decl(index.js, 0, 5))
6+
7+
export class foo1 {
8+
>foo1 : Symbol(foo1, Decl(index.js, 0, 20))
9+
10+
constructor() {
11+
this[prop] = 'bar'
12+
>this : Symbol(foo1, Decl(index.js, 0, 20))
13+
>prop : Symbol(prop, Decl(index.js, 0, 5))
14+
}
15+
16+
/**
17+
* @protected
18+
* @type {string}
19+
*/
20+
[prop] = 'baz';
21+
>[prop] : Symbol(foo1[prop], Decl(index.js, 5, 5))
22+
>prop : Symbol(prop, Decl(index.js, 0, 5))
23+
}
24+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//// [tests/cases/compiler/lateBoundAssignmentCandidateJS2.ts] ////
2+
3+
=== index.js ===
4+
const prop = 'prop';
5+
>prop : "prop"
6+
> : ^^^^^^
7+
>'prop' : "prop"
8+
> : ^^^^^^
9+
10+
export class foo1 {
11+
>foo1 : foo1
12+
> : ^^^^
13+
14+
constructor() {
15+
this[prop] = 'bar'
16+
>this[prop] = 'bar' : "bar"
17+
> : ^^^^^
18+
>this[prop] : string
19+
> : ^^^^^^
20+
>this : this
21+
> : ^^^^
22+
>prop : "prop"
23+
> : ^^^^^^
24+
>'bar' : "bar"
25+
> : ^^^^^
26+
}
27+
28+
/**
29+
* @protected
30+
* @type {string}
31+
*/
32+
[prop] = 'baz';
33+
>[prop] : string
34+
> : ^^^^^^
35+
>prop : "prop"
36+
> : ^^^^^^
37+
>'baz' : "baz"
38+
> : ^^^^^
39+
}
40+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
index.js(5,9): error TS2322: Type 'number' is not assignable to type 'string'.
2+
3+
4+
==== index.js (1 errors) ====
5+
const prop = 'prop';
6+
7+
export class foo2 {
8+
constructor() {
9+
this[prop] = 12;
10+
~~~~~~~~~~
11+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
12+
}
13+
14+
/**
15+
* @protected
16+
* @type {string}
17+
*/
18+
prop = 'baz';
19+
}
20+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//// [tests/cases/compiler/lateBoundAssignmentCandidateJS3.ts] ////
2+
3+
//// [index.js]
4+
const prop = 'prop';
5+
6+
export class foo2 {
7+
constructor() {
8+
this[prop] = 12;
9+
}
10+
11+
/**
12+
* @protected
13+
* @type {string}
14+
*/
15+
prop = 'baz';
16+
}
17+
18+
19+
//// [index.js]
20+
const prop = 'prop';
21+
export class foo2 {
22+
constructor() {
23+
this[prop] = 12;
24+
}
25+
/**
26+
* @protected
27+
* @type {string}
28+
*/
29+
prop = 'baz';
30+
}
31+
32+
33+
//// [index.d.ts]
34+
export class foo2 {
35+
/**
36+
* @protected
37+
* @type {string}
38+
*/
39+
protected prop: string;
40+
}

0 commit comments

Comments
 (0)