Skip to content

Commit b379e7f

Browse files
authored
Pass contextFlags when getting contextual type of JSX elements/attributes (#49707)
1 parent 1eb276f commit b379e7f

File tree

5 files changed

+202
-10
lines changed

5 files changed

+202
-10
lines changed

src/compiler/checker.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27037,8 +27037,8 @@ namespace ts {
2703727037
return node === conditional.whenTrue || node === conditional.whenFalse ? getContextualType(conditional, contextFlags) : undefined;
2703827038
}
2703927039

27040-
function getContextualTypeForChildJsxExpression(node: JsxElement, child: JsxChild) {
27041-
const attributesType = getApparentTypeOfContextualType(node.openingElement.tagName);
27040+
function getContextualTypeForChildJsxExpression(node: JsxElement, child: JsxChild, contextFlags?: ContextFlags) {
27041+
const attributesType = getApparentTypeOfContextualType(node.openingElement.tagName, contextFlags);
2704227042
// JSX expression is in children of JSX Element, we will look for an "children" attribute (we get the name from JSX.ElementAttributesProperty)
2704327043
const jsxChildrenPropertyName = getJsxElementChildrenPropertyName(getJsxNamespaceAt(node));
2704427044
if (!(attributesType && !isTypeAny(attributesType) && jsxChildrenPropertyName && jsxChildrenPropertyName !== "")) {
@@ -27057,28 +27057,28 @@ namespace ts {
2705727057
}, /*noReductions*/ true));
2705827058
}
2705927059

27060-
function getContextualTypeForJsxExpression(node: JsxExpression): Type | undefined {
27060+
function getContextualTypeForJsxExpression(node: JsxExpression, contextFlags?: ContextFlags): Type | undefined {
2706127061
const exprParent = node.parent;
2706227062
return isJsxAttributeLike(exprParent)
27063-
? getContextualType(node)
27063+
? getContextualType(node, contextFlags)
2706427064
: isJsxElement(exprParent)
27065-
? getContextualTypeForChildJsxExpression(exprParent, node)
27065+
? getContextualTypeForChildJsxExpression(exprParent, node, contextFlags)
2706627066
: undefined;
2706727067
}
2706827068

27069-
function getContextualTypeForJsxAttribute(attribute: JsxAttribute | JsxSpreadAttribute): Type | undefined {
27069+
function getContextualTypeForJsxAttribute(attribute: JsxAttribute | JsxSpreadAttribute, contextFlags?: ContextFlags): Type | undefined {
2707027070
// When we trying to resolve JsxOpeningLikeElement as a stateless function element, we will already give its attributes a contextual type
2707127071
// which is a type of the parameter of the signature we are trying out.
2707227072
// If there is no contextual type (e.g. we are trying to resolve stateful component), get attributes type from resolving element's tagName
2707327073
if (isJsxAttribute(attribute)) {
27074-
const attributesType = getApparentTypeOfContextualType(attribute.parent);
27074+
const attributesType = getApparentTypeOfContextualType(attribute.parent, contextFlags);
2707527075
if (!attributesType || isTypeAny(attributesType)) {
2707627076
return undefined;
2707727077
}
2707827078
return getTypeOfPropertyOfContextualType(attributesType, attribute.name.escapedText);
2707927079
}
2708027080
else {
27081-
return getContextualType(attribute.parent);
27081+
return getContextualType(attribute.parent, contextFlags);
2708227082
}
2708327083
}
2708427084

@@ -27272,10 +27272,10 @@ namespace ts {
2727227272
case SyntaxKind.ExportAssignment:
2727327273
return tryGetTypeFromEffectiveTypeNode(parent as ExportAssignment);
2727427274
case SyntaxKind.JsxExpression:
27275-
return getContextualTypeForJsxExpression(parent as JsxExpression);
27275+
return getContextualTypeForJsxExpression(parent as JsxExpression, contextFlags);
2727627276
case SyntaxKind.JsxAttribute:
2727727277
case SyntaxKind.JsxSpreadAttribute:
27278-
return getContextualTypeForJsxAttribute(parent as JsxAttribute | JsxSpreadAttribute);
27278+
return getContextualTypeForJsxAttribute(parent as JsxAttribute | JsxSpreadAttribute, contextFlags);
2727927279
case SyntaxKind.JsxOpeningElement:
2728027280
case SyntaxKind.JsxSelfClosingElement:
2728127281
return getContextualJsxElementAttributesType(parent as JsxOpeningLikeElement, contextFlags);
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//// [index.tsx]
2+
interface Elements {
3+
foo: { callback?: (value: number) => void };
4+
bar: { callback?: (value: string) => void };
5+
}
6+
7+
type Props<C extends keyof Elements> = { as?: C } & Elements[C];
8+
declare function Test<C extends keyof Elements>(props: Props<C>): null;
9+
10+
<Test
11+
as="bar"
12+
callback={(value) => {}}
13+
/>;
14+
15+
Test({
16+
as: "bar",
17+
callback: (value) => {},
18+
});
19+
20+
<Test<'bar'>
21+
as="bar"
22+
callback={(value) => {}}
23+
/>;
24+
25+
26+
//// [index.jsx]
27+
<Test as="bar" callback={function (value) { }}/>;
28+
Test({
29+
as: "bar",
30+
callback: function (value) { }
31+
});
32+
<Test as="bar" callback={function (value) { }}/>;
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
=== tests/cases/compiler/index.tsx ===
2+
interface Elements {
3+
>Elements : Symbol(Elements, Decl(index.tsx, 0, 0))
4+
5+
foo: { callback?: (value: number) => void };
6+
>foo : Symbol(Elements.foo, Decl(index.tsx, 0, 20))
7+
>callback : Symbol(callback, Decl(index.tsx, 1, 8))
8+
>value : Symbol(value, Decl(index.tsx, 1, 21))
9+
10+
bar: { callback?: (value: string) => void };
11+
>bar : Symbol(Elements.bar, Decl(index.tsx, 1, 46))
12+
>callback : Symbol(callback, Decl(index.tsx, 2, 8))
13+
>value : Symbol(value, Decl(index.tsx, 2, 21))
14+
}
15+
16+
type Props<C extends keyof Elements> = { as?: C } & Elements[C];
17+
>Props : Symbol(Props, Decl(index.tsx, 3, 1))
18+
>C : Symbol(C, Decl(index.tsx, 5, 11))
19+
>Elements : Symbol(Elements, Decl(index.tsx, 0, 0))
20+
>as : Symbol(as, Decl(index.tsx, 5, 40))
21+
>C : Symbol(C, Decl(index.tsx, 5, 11))
22+
>Elements : Symbol(Elements, Decl(index.tsx, 0, 0))
23+
>C : Symbol(C, Decl(index.tsx, 5, 11))
24+
25+
declare function Test<C extends keyof Elements>(props: Props<C>): null;
26+
>Test : Symbol(Test, Decl(index.tsx, 5, 64))
27+
>C : Symbol(C, Decl(index.tsx, 6, 22))
28+
>Elements : Symbol(Elements, Decl(index.tsx, 0, 0))
29+
>props : Symbol(props, Decl(index.tsx, 6, 48))
30+
>Props : Symbol(Props, Decl(index.tsx, 3, 1))
31+
>C : Symbol(C, Decl(index.tsx, 6, 22))
32+
33+
<Test
34+
>Test : Symbol(Test, Decl(index.tsx, 5, 64))
35+
36+
as="bar"
37+
>as : Symbol(as, Decl(index.tsx, 8, 5))
38+
39+
callback={(value) => {}}
40+
>callback : Symbol(callback, Decl(index.tsx, 9, 10))
41+
>value : Symbol(value, Decl(index.tsx, 10, 13))
42+
43+
/>;
44+
45+
Test({
46+
>Test : Symbol(Test, Decl(index.tsx, 5, 64))
47+
48+
as: "bar",
49+
>as : Symbol(as, Decl(index.tsx, 13, 6))
50+
51+
callback: (value) => {},
52+
>callback : Symbol(callback, Decl(index.tsx, 14, 12))
53+
>value : Symbol(value, Decl(index.tsx, 15, 13))
54+
55+
});
56+
57+
<Test<'bar'>
58+
>Test : Symbol(Test, Decl(index.tsx, 5, 64))
59+
60+
as="bar"
61+
>as : Symbol(as, Decl(index.tsx, 18, 12))
62+
63+
callback={(value) => {}}
64+
>callback : Symbol(callback, Decl(index.tsx, 19, 10))
65+
>value : Symbol(value, Decl(index.tsx, 20, 13))
66+
67+
/>;
68+
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
=== tests/cases/compiler/index.tsx ===
2+
interface Elements {
3+
foo: { callback?: (value: number) => void };
4+
>foo : { callback?: (value: number) => void; }
5+
>callback : (value: number) => void
6+
>value : number
7+
8+
bar: { callback?: (value: string) => void };
9+
>bar : { callback?: (value: string) => void; }
10+
>callback : (value: string) => void
11+
>value : string
12+
}
13+
14+
type Props<C extends keyof Elements> = { as?: C } & Elements[C];
15+
>Props : Props<C>
16+
>as : C
17+
18+
declare function Test<C extends keyof Elements>(props: Props<C>): null;
19+
>Test : <C extends keyof Elements>(props: Props<C>) => null
20+
>props : Props<C>
21+
>null : null
22+
23+
<Test
24+
><Test as="bar" callback={(value) => {}}/> : error
25+
>Test : <C extends keyof Elements>(props: Props<C>) => null
26+
27+
as="bar"
28+
>as : "bar"
29+
30+
callback={(value) => {}}
31+
>callback : (value: string) => void
32+
>(value) => {} : (value: string) => void
33+
>value : string
34+
35+
/>;
36+
37+
Test({
38+
>Test({ as: "bar", callback: (value) => {},}) : null
39+
>Test : <C extends keyof Elements>(props: Props<C>) => null
40+
>{ as: "bar", callback: (value) => {},} : { as: "bar"; callback: (value: string) => void; }
41+
42+
as: "bar",
43+
>as : "bar"
44+
>"bar" : "bar"
45+
46+
callback: (value) => {},
47+
>callback : (value: string) => void
48+
>(value) => {} : (value: string) => void
49+
>value : string
50+
51+
});
52+
53+
<Test<'bar'>
54+
><Test<'bar'> as="bar" callback={(value) => {}}/> : error
55+
>Test : <C extends keyof Elements>(props: Props<C>) => null
56+
57+
as="bar"
58+
>as : "bar"
59+
60+
callback={(value) => {}}
61+
>callback : (value: string) => void
62+
>(value) => {} : (value: string) => void
63+
>value : string
64+
65+
/>;
66+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// @jsx: preserve
2+
// @noImplicitAny: true
3+
4+
// @filename: index.tsx
5+
interface Elements {
6+
foo: { callback?: (value: number) => void };
7+
bar: { callback?: (value: string) => void };
8+
}
9+
10+
type Props<C extends keyof Elements> = { as?: C } & Elements[C];
11+
declare function Test<C extends keyof Elements>(props: Props<C>): null;
12+
13+
<Test
14+
as="bar"
15+
callback={(value) => {}}
16+
/>;
17+
18+
Test({
19+
as: "bar",
20+
callback: (value) => {},
21+
});
22+
23+
<Test<'bar'>
24+
as="bar"
25+
callback={(value) => {}}
26+
/>;

0 commit comments

Comments
 (0)