Skip to content

Commit 0258db2

Browse files
author
Orta
authored
Adds support for looking up past Blocks in expando objects (microsoft#38031)
* Adds support for looking up past Blocks in expando objects * Adds JS tests to validate the JS parsing also works * Get the top level block expando tests green
1 parent d9ad27f commit 0258db2

File tree

5 files changed

+275
-3
lines changed

5 files changed

+275
-3
lines changed

src/compiler/binder.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2964,7 +2964,7 @@ namespace ts {
29642964

29652965
function bindSpecialPropertyAssignment(node: BindablePropertyAssignmentExpression) {
29662966
// Class declarations in Typescript do not allow property declarations
2967-
const parentSymbol = lookupSymbolForPropertyAccess(node.left.expression);
2967+
const parentSymbol = lookupSymbolForPropertyAccess(node.left.expression, container) || lookupSymbolForPropertyAccess(node.left.expression, blockScopeContainer) ;
29682968
if (!isInJSFile(node) && !isFunctionSymbol(parentSymbol)) {
29692969
return;
29702970
}
@@ -3073,7 +3073,7 @@ namespace ts {
30733073
}
30743074

30753075
function bindPropertyAssignment(name: BindableStaticNameExpression, propertyAccess: BindableStaticAccessExpression, isPrototypeProperty: boolean, containerIsClass: boolean) {
3076-
let namespaceSymbol = lookupSymbolForPropertyAccess(name);
3076+
let namespaceSymbol = lookupSymbolForPropertyAccess(name, container) || lookupSymbolForPropertyAccess(name, blockScopeContainer);
30773077
const isToplevel = isTopLevelNamespaceAssignment(propertyAccess);
30783078
namespaceSymbol = bindPotentiallyMissingNamespaces(namespaceSymbol, propertyAccess.expression, isToplevel, isPrototypeProperty, containerIsClass);
30793079
bindPotentiallyNewExpandoMemberToNamespace(propertyAccess, namespaceSymbol, isPrototypeProperty);

src/compiler/utilities.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2016,7 +2016,7 @@ namespace ts {
20162016

20172017
/**
20182018
* Get the assignment 'initializer' -- the righthand side-- when the initializer is container-like (See getExpandoInitializer).
2019-
* We treat the right hand side of assignments with container-like initalizers as declarations.
2019+
* We treat the right hand side of assignments with container-like initializers as declarations.
20202020
*/
20212021
export function getAssignedExpandoInitializer(node: Node | undefined): Expression | undefined {
20222022
if (node && node.parent && isBinaryExpression(node.parent) && node.parent.operatorToken.kind === SyntaxKind.EqualsToken) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
=== tests/cases/compiler/check.ts ===
2+
// https://github.com/microsoft/TypeScript/issues/31972
3+
4+
5+
6+
// https://github.com/microsoft/TypeScript/issues/31972
7+
interface Person {
8+
>Person : Symbol(Person, Decl(check.ts, 0, 0))
9+
10+
first: string;
11+
>first : Symbol(Person.first, Decl(check.ts, 5, 18))
12+
13+
last: string;
14+
>last : Symbol(Person.last, Decl(check.ts, 6, 16))
15+
}
16+
17+
{
18+
const dice = () => Math.floor(Math.random() * 6);
19+
>dice : Symbol(dice, Decl(check.ts, 11, 7))
20+
>Math.floor : Symbol(Math.floor, Decl(lib.es5.d.ts, --, --))
21+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
22+
>floor : Symbol(Math.floor, Decl(lib.es5.d.ts, --, --))
23+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
24+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
25+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
26+
27+
dice.first = 'Rando';
28+
>dice.first : Symbol(dice.first, Decl(check.ts, 11, 51))
29+
>dice : Symbol(dice, Decl(check.ts, 11, 7))
30+
>first : Symbol(dice.first, Decl(check.ts, 11, 51))
31+
32+
dice.last = 'Calrissian';
33+
>dice.last : Symbol(dice.last, Decl(check.ts, 12, 23))
34+
>dice : Symbol(dice, Decl(check.ts, 11, 7))
35+
>last : Symbol(dice.last, Decl(check.ts, 12, 23))
36+
37+
const diceP: Person = dice;
38+
>diceP : Symbol(diceP, Decl(check.ts, 14, 7))
39+
>Person : Symbol(Person, Decl(check.ts, 0, 0))
40+
>dice : Symbol(dice, Decl(check.ts, 11, 7))
41+
}
42+
43+
=== tests/cases/compiler/check.js ===
44+
// Creates a type { first:string, last: string }
45+
/**
46+
* @typedef {Object} Human - creates a new type named 'SpecialType'
47+
* @property {string} first - a string property of SpecialType
48+
* @property {string} last - a number property of SpecialType
49+
*/
50+
51+
/**
52+
* @param {Human} param used as a validation tool
53+
*/
54+
function doHumanThings(param) {}
55+
>doHumanThings : Symbol(doHumanThings, Decl(check.js, 0, 0))
56+
>param : Symbol(param, Decl(check.js, 10, 23))
57+
58+
const dice1 = () => Math.floor(Math.random() * 6);
59+
>dice1 : Symbol(dice1, Decl(check.js, 12, 5), Decl(check.js, 12, 50))
60+
>Math.floor : Symbol(Math.floor, Decl(lib.es5.d.ts, --, --))
61+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
62+
>floor : Symbol(Math.floor, Decl(lib.es5.d.ts, --, --))
63+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
64+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
65+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
66+
67+
// dice1.first = 'Rando';
68+
dice1.last = 'Calrissian';
69+
>dice1.last : Symbol(dice1.last, Decl(check.js, 12, 50))
70+
>dice1 : Symbol(dice1, Decl(check.js, 12, 5), Decl(check.js, 12, 50))
71+
>last : Symbol(dice1.last, Decl(check.js, 12, 50))
72+
73+
// doHumanThings(dice)
74+
75+
// but inside a block... you can't call a human
76+
{
77+
const dice2 = () => Math.floor(Math.random() * 6);
78+
>dice2 : Symbol(dice2, Decl(check.js, 20, 7))
79+
>Math.floor : Symbol(Math.floor, Decl(lib.es5.d.ts, --, --))
80+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
81+
>floor : Symbol(Math.floor, Decl(lib.es5.d.ts, --, --))
82+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
83+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
84+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
85+
86+
dice2.first = 'Rando';
87+
>dice2.first : Symbol(dice2.first, Decl(check.js, 20, 52))
88+
>dice2 : Symbol(dice2, Decl(check.js, 20, 7))
89+
>first : Symbol(dice2.first, Decl(check.js, 20, 52))
90+
91+
dice2.last = 'Calrissian';
92+
>dice2.last : Symbol(dice2.last, Decl(check.js, 21, 24))
93+
>dice2 : Symbol(dice2, Decl(check.js, 20, 7))
94+
>last : Symbol(dice2.last, Decl(check.js, 21, 24))
95+
96+
doHumanThings(dice2)
97+
>doHumanThings : Symbol(doHumanThings, Decl(check.js, 0, 0))
98+
>dice2 : Symbol(dice2, Decl(check.js, 20, 7))
99+
}
100+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
=== tests/cases/compiler/check.ts ===
2+
// https://github.com/microsoft/TypeScript/issues/31972
3+
4+
5+
6+
// https://github.com/microsoft/TypeScript/issues/31972
7+
interface Person {
8+
first: string;
9+
>first : string
10+
11+
last: string;
12+
>last : string
13+
}
14+
15+
{
16+
const dice = () => Math.floor(Math.random() * 6);
17+
>dice : { (): number; first: string; last: string; }
18+
>() => Math.floor(Math.random() * 6) : { (): number; first: string; last: string; }
19+
>Math.floor(Math.random() * 6) : number
20+
>Math.floor : (x: number) => number
21+
>Math : Math
22+
>floor : (x: number) => number
23+
>Math.random() * 6 : number
24+
>Math.random() : number
25+
>Math.random : () => number
26+
>Math : Math
27+
>random : () => number
28+
>6 : 6
29+
30+
dice.first = 'Rando';
31+
>dice.first = 'Rando' : "Rando"
32+
>dice.first : string
33+
>dice : { (): number; first: string; last: string; }
34+
>first : string
35+
>'Rando' : "Rando"
36+
37+
dice.last = 'Calrissian';
38+
>dice.last = 'Calrissian' : "Calrissian"
39+
>dice.last : string
40+
>dice : { (): number; first: string; last: string; }
41+
>last : string
42+
>'Calrissian' : "Calrissian"
43+
44+
const diceP: Person = dice;
45+
>diceP : Person
46+
>dice : { (): number; first: string; last: string; }
47+
}
48+
49+
=== tests/cases/compiler/check.js ===
50+
// Creates a type { first:string, last: string }
51+
/**
52+
* @typedef {Object} Human - creates a new type named 'SpecialType'
53+
* @property {string} first - a string property of SpecialType
54+
* @property {string} last - a number property of SpecialType
55+
*/
56+
57+
/**
58+
* @param {Human} param used as a validation tool
59+
*/
60+
function doHumanThings(param) {}
61+
>doHumanThings : (param: Human) => void
62+
>param : Human
63+
64+
const dice1 = () => Math.floor(Math.random() * 6);
65+
>dice1 : { (): number; last: string; }
66+
>() => Math.floor(Math.random() * 6) : { (): number; last: string; }
67+
>Math.floor(Math.random() * 6) : number
68+
>Math.floor : (x: number) => number
69+
>Math : Math
70+
>floor : (x: number) => number
71+
>Math.random() * 6 : number
72+
>Math.random() : number
73+
>Math.random : () => number
74+
>Math : Math
75+
>random : () => number
76+
>6 : 6
77+
78+
// dice1.first = 'Rando';
79+
dice1.last = 'Calrissian';
80+
>dice1.last = 'Calrissian' : "Calrissian"
81+
>dice1.last : string
82+
>dice1 : { (): number; last: string; }
83+
>last : string
84+
>'Calrissian' : "Calrissian"
85+
86+
// doHumanThings(dice)
87+
88+
// but inside a block... you can't call a human
89+
{
90+
const dice2 = () => Math.floor(Math.random() * 6);
91+
>dice2 : { (): number; first: string; last: string; }
92+
>() => Math.floor(Math.random() * 6) : { (): number; first: string; last: string; }
93+
>Math.floor(Math.random() * 6) : number
94+
>Math.floor : (x: number) => number
95+
>Math : Math
96+
>floor : (x: number) => number
97+
>Math.random() * 6 : number
98+
>Math.random() : number
99+
>Math.random : () => number
100+
>Math : Math
101+
>random : () => number
102+
>6 : 6
103+
104+
dice2.first = 'Rando';
105+
>dice2.first = 'Rando' : "Rando"
106+
>dice2.first : string
107+
>dice2 : { (): number; first: string; last: string; }
108+
>first : string
109+
>'Rando' : "Rando"
110+
111+
dice2.last = 'Calrissian';
112+
>dice2.last = 'Calrissian' : "Calrissian"
113+
>dice2.last : string
114+
>dice2 : { (): number; first: string; last: string; }
115+
>last : string
116+
>'Calrissian' : "Calrissian"
117+
118+
doHumanThings(dice2)
119+
>doHumanThings(dice2) : void
120+
>doHumanThings : (param: Human) => void
121+
>dice2 : { (): number; first: string; last: string; }
122+
}
123+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// https://github.com/microsoft/TypeScript/issues/31972
2+
3+
// @allowJs: true
4+
// @noEmit: true
5+
// @checkJs: true
6+
7+
// @filename: check.ts
8+
9+
// https://github.com/microsoft/TypeScript/issues/31972
10+
interface Person {
11+
first: string;
12+
last: string;
13+
}
14+
15+
{
16+
const dice = () => Math.floor(Math.random() * 6);
17+
dice.first = 'Rando';
18+
dice.last = 'Calrissian';
19+
const diceP: Person = dice;
20+
}
21+
22+
// @filename: check.js
23+
24+
// Creates a type { first:string, last: string }
25+
/**
26+
* @typedef {Object} Human - creates a new type named 'SpecialType'
27+
* @property {string} first - a string property of SpecialType
28+
* @property {string} last - a number property of SpecialType
29+
*/
30+
31+
/**
32+
* @param {Human} param used as a validation tool
33+
*/
34+
function doHumanThings(param) {}
35+
36+
const dice1 = () => Math.floor(Math.random() * 6);
37+
// dice1.first = 'Rando';
38+
dice1.last = 'Calrissian';
39+
40+
// doHumanThings(dice)
41+
42+
// but inside a block... you can't call a human
43+
{
44+
const dice2 = () => Math.floor(Math.random() * 6);
45+
dice2.first = 'Rando';
46+
dice2.last = 'Calrissian';
47+
48+
doHumanThings(dice2)
49+
}

0 commit comments

Comments
 (0)