Skip to content

Commit 420c788

Browse files
ahejlsbergmprobst
authored andcommitted
Fix stack overflow in JSX discriminated union logic (microsoft#46354)
* Use getContextFreeTypeOfExpression to avoid circularities * Add regression test
1 parent 6f4bd5f commit 420c788

File tree

5 files changed

+208
-2
lines changed

5 files changed

+208
-2
lines changed

src/compiler/checker.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -22691,7 +22691,7 @@ namespace ts {
2269122691
const keyPropertyName = getKeyPropertyName(unionType);
2269222692
const propNode = keyPropertyName && find(node.properties, p => p.symbol && p.kind === SyntaxKind.PropertyAssignment &&
2269322693
p.symbol.escapedName === keyPropertyName && isPossiblyDiscriminantValue(p.initializer));
22694-
const propType = propNode && getTypeOfExpression((propNode as PropertyAssignment).initializer);
22694+
const propType = propNode && getContextFreeTypeOfExpression((propNode as PropertyAssignment).initializer);
2269522695
return propType && getConstituentTypeForKeyType(unionType, propType);
2269622696
}
2269722697

@@ -26198,7 +26198,7 @@ namespace ts {
2619826198
concatenate(
2619926199
map(
2620026200
filter(node.properties, p => !!p.symbol && p.kind === SyntaxKind.JsxAttribute && isDiscriminantProperty(contextualType, p.symbol.escapedName) && (!p.initializer || isPossiblyDiscriminantValue(p.initializer))),
26201-
prop => ([!(prop as JsxAttribute).initializer ? (() => trueType) : (() => checkExpression((prop as JsxAttribute).initializer!)), prop.symbol.escapedName] as [() => Type, __String])
26201+
prop => ([!(prop as JsxAttribute).initializer ? (() => trueType) : (() => getContextFreeTypeOfExpression((prop as JsxAttribute).initializer!)), prop.symbol.escapedName] as [() => Type, __String])
2620226202
),
2620326203
map(
2620426204
filter(getPropertiesOfType(contextualType), s => !!(s.flags & SymbolFlags.Optional) && !!node?.symbol?.members && !node.symbol.members.has(s.escapedName) && isDiscriminantProperty(contextualType, s.escapedName)),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//// [discriminatedUnionJsxElement.tsx]
2+
// Repro from #46021
3+
4+
interface IData<MenuItemVariant extends ListItemVariant = ListItemVariant.OneLine> {
5+
menuItemsVariant?: MenuItemVariant;
6+
}
7+
8+
function Menu<MenuItemVariant extends ListItemVariant = ListItemVariant.OneLine>(data: IData<MenuItemVariant>) {
9+
const listItemVariant = data.menuItemsVariant ?? ListItemVariant.OneLine;
10+
return <ListItem variant={listItemVariant} />;
11+
}
12+
13+
type IListItemData = { variant: ListItemVariant.Avatar; } | { variant: ListItemVariant.OneLine; };
14+
15+
enum ListItemVariant {
16+
OneLine,
17+
Avatar,
18+
}
19+
20+
function ListItem(_data: IListItemData) {
21+
return null;
22+
}
23+
24+
25+
//// [discriminatedUnionJsxElement.jsx]
26+
"use strict";
27+
// Repro from #46021
28+
function Menu(data) {
29+
var _a;
30+
var listItemVariant = (_a = data.menuItemsVariant) !== null && _a !== void 0 ? _a : ListItemVariant.OneLine;
31+
return <ListItem variant={listItemVariant}/>;
32+
}
33+
var ListItemVariant;
34+
(function (ListItemVariant) {
35+
ListItemVariant[ListItemVariant["OneLine"] = 0] = "OneLine";
36+
ListItemVariant[ListItemVariant["Avatar"] = 1] = "Avatar";
37+
})(ListItemVariant || (ListItemVariant = {}));
38+
function ListItem(_data) {
39+
return null;
40+
}
41+
42+
43+
//// [discriminatedUnionJsxElement.d.ts]
44+
interface IData<MenuItemVariant extends ListItemVariant = ListItemVariant.OneLine> {
45+
menuItemsVariant?: MenuItemVariant;
46+
}
47+
declare function Menu<MenuItemVariant extends ListItemVariant = ListItemVariant.OneLine>(data: IData<MenuItemVariant>): any;
48+
declare type IListItemData = {
49+
variant: ListItemVariant.Avatar;
50+
} | {
51+
variant: ListItemVariant.OneLine;
52+
};
53+
declare enum ListItemVariant {
54+
OneLine = 0,
55+
Avatar = 1
56+
}
57+
declare function ListItem(_data: IListItemData): null;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
=== tests/cases/compiler/discriminatedUnionJsxElement.tsx ===
2+
// Repro from #46021
3+
4+
interface IData<MenuItemVariant extends ListItemVariant = ListItemVariant.OneLine> {
5+
>IData : Symbol(IData, Decl(discriminatedUnionJsxElement.tsx, 0, 0))
6+
>MenuItemVariant : Symbol(MenuItemVariant, Decl(discriminatedUnionJsxElement.tsx, 2, 16))
7+
>ListItemVariant : Symbol(ListItemVariant, Decl(discriminatedUnionJsxElement.tsx, 11, 98))
8+
>ListItemVariant : Symbol(ListItemVariant, Decl(discriminatedUnionJsxElement.tsx, 11, 98))
9+
>OneLine : Symbol(ListItemVariant.OneLine, Decl(discriminatedUnionJsxElement.tsx, 13, 22))
10+
11+
menuItemsVariant?: MenuItemVariant;
12+
>menuItemsVariant : Symbol(IData.menuItemsVariant, Decl(discriminatedUnionJsxElement.tsx, 2, 84))
13+
>MenuItemVariant : Symbol(MenuItemVariant, Decl(discriminatedUnionJsxElement.tsx, 2, 16))
14+
}
15+
16+
function Menu<MenuItemVariant extends ListItemVariant = ListItemVariant.OneLine>(data: IData<MenuItemVariant>) {
17+
>Menu : Symbol(Menu, Decl(discriminatedUnionJsxElement.tsx, 4, 1))
18+
>MenuItemVariant : Symbol(MenuItemVariant, Decl(discriminatedUnionJsxElement.tsx, 6, 14))
19+
>ListItemVariant : Symbol(ListItemVariant, Decl(discriminatedUnionJsxElement.tsx, 11, 98))
20+
>ListItemVariant : Symbol(ListItemVariant, Decl(discriminatedUnionJsxElement.tsx, 11, 98))
21+
>OneLine : Symbol(ListItemVariant.OneLine, Decl(discriminatedUnionJsxElement.tsx, 13, 22))
22+
>data : Symbol(data, Decl(discriminatedUnionJsxElement.tsx, 6, 81))
23+
>IData : Symbol(IData, Decl(discriminatedUnionJsxElement.tsx, 0, 0))
24+
>MenuItemVariant : Symbol(MenuItemVariant, Decl(discriminatedUnionJsxElement.tsx, 6, 14))
25+
26+
const listItemVariant = data.menuItemsVariant ?? ListItemVariant.OneLine;
27+
>listItemVariant : Symbol(listItemVariant, Decl(discriminatedUnionJsxElement.tsx, 7, 9))
28+
>data.menuItemsVariant : Symbol(IData.menuItemsVariant, Decl(discriminatedUnionJsxElement.tsx, 2, 84))
29+
>data : Symbol(data, Decl(discriminatedUnionJsxElement.tsx, 6, 81))
30+
>menuItemsVariant : Symbol(IData.menuItemsVariant, Decl(discriminatedUnionJsxElement.tsx, 2, 84))
31+
>ListItemVariant.OneLine : Symbol(ListItemVariant.OneLine, Decl(discriminatedUnionJsxElement.tsx, 13, 22))
32+
>ListItemVariant : Symbol(ListItemVariant, Decl(discriminatedUnionJsxElement.tsx, 11, 98))
33+
>OneLine : Symbol(ListItemVariant.OneLine, Decl(discriminatedUnionJsxElement.tsx, 13, 22))
34+
35+
return <ListItem variant={listItemVariant} />;
36+
>ListItem : Symbol(ListItem, Decl(discriminatedUnionJsxElement.tsx, 16, 1))
37+
>variant : Symbol(variant, Decl(discriminatedUnionJsxElement.tsx, 8, 20))
38+
>listItemVariant : Symbol(listItemVariant, Decl(discriminatedUnionJsxElement.tsx, 7, 9))
39+
}
40+
41+
type IListItemData = { variant: ListItemVariant.Avatar; } | { variant: ListItemVariant.OneLine; };
42+
>IListItemData : Symbol(IListItemData, Decl(discriminatedUnionJsxElement.tsx, 9, 1))
43+
>variant : Symbol(variant, Decl(discriminatedUnionJsxElement.tsx, 11, 22))
44+
>ListItemVariant : Symbol(ListItemVariant, Decl(discriminatedUnionJsxElement.tsx, 11, 98))
45+
>Avatar : Symbol(ListItemVariant.Avatar, Decl(discriminatedUnionJsxElement.tsx, 14, 12))
46+
>variant : Symbol(variant, Decl(discriminatedUnionJsxElement.tsx, 11, 61))
47+
>ListItemVariant : Symbol(ListItemVariant, Decl(discriminatedUnionJsxElement.tsx, 11, 98))
48+
>OneLine : Symbol(ListItemVariant.OneLine, Decl(discriminatedUnionJsxElement.tsx, 13, 22))
49+
50+
enum ListItemVariant {
51+
>ListItemVariant : Symbol(ListItemVariant, Decl(discriminatedUnionJsxElement.tsx, 11, 98))
52+
53+
OneLine,
54+
>OneLine : Symbol(ListItemVariant.OneLine, Decl(discriminatedUnionJsxElement.tsx, 13, 22))
55+
56+
Avatar,
57+
>Avatar : Symbol(ListItemVariant.Avatar, Decl(discriminatedUnionJsxElement.tsx, 14, 12))
58+
}
59+
60+
function ListItem(_data: IListItemData) {
61+
>ListItem : Symbol(ListItem, Decl(discriminatedUnionJsxElement.tsx, 16, 1))
62+
>_data : Symbol(_data, Decl(discriminatedUnionJsxElement.tsx, 18, 18))
63+
>IListItemData : Symbol(IListItemData, Decl(discriminatedUnionJsxElement.tsx, 9, 1))
64+
65+
return null;
66+
}
67+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
=== tests/cases/compiler/discriminatedUnionJsxElement.tsx ===
2+
// Repro from #46021
3+
4+
interface IData<MenuItemVariant extends ListItemVariant = ListItemVariant.OneLine> {
5+
>ListItemVariant : any
6+
7+
menuItemsVariant?: MenuItemVariant;
8+
>menuItemsVariant : MenuItemVariant | undefined
9+
}
10+
11+
function Menu<MenuItemVariant extends ListItemVariant = ListItemVariant.OneLine>(data: IData<MenuItemVariant>) {
12+
>Menu : <MenuItemVariant extends ListItemVariant = ListItemVariant.OneLine>(data: IData<MenuItemVariant>) => any
13+
>ListItemVariant : any
14+
>data : IData<MenuItemVariant>
15+
16+
const listItemVariant = data.menuItemsVariant ?? ListItemVariant.OneLine;
17+
>listItemVariant : ListItemVariant.OneLine | NonNullable<MenuItemVariant>
18+
>data.menuItemsVariant ?? ListItemVariant.OneLine : ListItemVariant.OneLine | NonNullable<MenuItemVariant>
19+
>data.menuItemsVariant : MenuItemVariant | undefined
20+
>data : IData<MenuItemVariant>
21+
>menuItemsVariant : MenuItemVariant | undefined
22+
>ListItemVariant.OneLine : ListItemVariant.OneLine
23+
>ListItemVariant : typeof ListItemVariant
24+
>OneLine : ListItemVariant.OneLine
25+
26+
return <ListItem variant={listItemVariant} />;
27+
><ListItem variant={listItemVariant} /> : error
28+
>ListItem : (_data: IListItemData) => null
29+
>variant : ListItemVariant
30+
>listItemVariant : ListItemVariant
31+
}
32+
33+
type IListItemData = { variant: ListItemVariant.Avatar; } | { variant: ListItemVariant.OneLine; };
34+
>IListItemData : IListItemData
35+
>variant : ListItemVariant.Avatar
36+
>ListItemVariant : any
37+
>variant : ListItemVariant.OneLine
38+
>ListItemVariant : any
39+
40+
enum ListItemVariant {
41+
>ListItemVariant : ListItemVariant
42+
43+
OneLine,
44+
>OneLine : ListItemVariant.OneLine
45+
46+
Avatar,
47+
>Avatar : ListItemVariant.Avatar
48+
}
49+
50+
function ListItem(_data: IListItemData) {
51+
>ListItem : (_data: IListItemData) => null
52+
>_data : IListItemData
53+
54+
return null;
55+
>null : null
56+
}
57+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// @strict: true
2+
// @declaration: true
3+
// @jsx: preserve
4+
5+
// Repro from #46021
6+
7+
interface IData<MenuItemVariant extends ListItemVariant = ListItemVariant.OneLine> {
8+
menuItemsVariant?: MenuItemVariant;
9+
}
10+
11+
function Menu<MenuItemVariant extends ListItemVariant = ListItemVariant.OneLine>(data: IData<MenuItemVariant>) {
12+
const listItemVariant = data.menuItemsVariant ?? ListItemVariant.OneLine;
13+
return <ListItem variant={listItemVariant} />;
14+
}
15+
16+
type IListItemData = { variant: ListItemVariant.Avatar; } | { variant: ListItemVariant.OneLine; };
17+
18+
enum ListItemVariant {
19+
OneLine,
20+
Avatar,
21+
}
22+
23+
function ListItem(_data: IListItemData) {
24+
return null;
25+
}

0 commit comments

Comments
 (0)