Skip to content

Commit 2cb7ab9

Browse files
Merge pull request #6232 from Microsoft/contextuallyTypeJsxStringLiteralAttributes
Contextually type JSX string literal attributes
2 parents 669e733 + 6c8bb63 commit 2cb7ab9

File tree

4 files changed

+86
-13
lines changed

4 files changed

+86
-13
lines changed

Diff for: src/compiler/checker.ts

+13-13
Original file line numberDiff line numberDiff line change
@@ -7474,24 +7474,22 @@ namespace ts {
74747474
return node === conditional.whenTrue || node === conditional.whenFalse ? getContextualType(conditional) : undefined;
74757475
}
74767476

7477-
function getContextualTypeForJsxExpression(expr: JsxExpression | JsxSpreadAttribute): Type {
7478-
// Contextual type only applies to JSX expressions that are in attribute assignments (not in 'Children' positions)
7479-
if (expr.parent.kind === SyntaxKind.JsxAttribute) {
7480-
const attrib = <JsxAttribute>expr.parent;
7481-
const attrsType = getJsxElementAttributesType(<JsxOpeningLikeElement>attrib.parent);
7477+
function getContextualTypeForJsxAttribute(attribute: JsxAttribute | JsxSpreadAttribute) {
7478+
const kind = attribute.kind;
7479+
const jsxElement = attribute.parent as JsxOpeningLikeElement;
7480+
const attrsType = getJsxElementAttributesType(jsxElement);
7481+
7482+
if (attribute.kind === SyntaxKind.JsxAttribute) {
74827483
if (!attrsType || isTypeAny(attrsType)) {
74837484
return undefined;
74847485
}
7485-
else {
7486-
return getTypeOfPropertyOfType(attrsType, attrib.name.text);
7487-
}
7486+
return getTypeOfPropertyOfType(attrsType, (attribute as JsxAttribute).name.text);
74887487
}
7489-
7490-
if (expr.kind === SyntaxKind.JsxSpreadAttribute) {
7491-
return getJsxElementAttributesType(<JsxOpeningLikeElement>expr.parent);
7488+
else if (attribute.kind === SyntaxKind.JsxSpreadAttribute) {
7489+
return attrsType;
74927490
}
74937491

7494-
return undefined;
7492+
Debug.fail(`Expected JsxAttribute or JsxSpreadAttribute, got ts.SyntaxKind[${kind}]`);
74957493
}
74967494

74977495
// Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily
@@ -7559,8 +7557,10 @@ namespace ts {
75597557
case SyntaxKind.ParenthesizedExpression:
75607558
return getContextualType(<ParenthesizedExpression>parent);
75617559
case SyntaxKind.JsxExpression:
7560+
return getContextualType(<JsxExpression>parent);
7561+
case SyntaxKind.JsxAttribute:
75627562
case SyntaxKind.JsxSpreadAttribute:
7563-
return getContextualTypeForJsxExpression(<JsxExpression>parent);
7563+
return getContextualTypeForJsxAttribute(<JsxAttribute | JsxSpreadAttribute>parent);
75647564
}
75657565
return undefined;
75667566
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
tests/cases/conformance/types/contextualTypes/jsxAttributes/contextuallyTypedStringLiteralsInJsxAttributes01.tsx(13,15): error TS2322: Type '"f"' is not assignable to type '"A" | "B" | "C"'.
2+
Type '"f"' is not assignable to type '"C"'.
3+
tests/cases/conformance/types/contextualTypes/jsxAttributes/contextuallyTypedStringLiteralsInJsxAttributes01.tsx(14,15): error TS2322: Type '"f"' is not assignable to type '"A" | "B" | "C"'.
4+
Type '"f"' is not assignable to type '"C"'.
5+
6+
7+
==== tests/cases/conformance/types/contextualTypes/jsxAttributes/contextuallyTypedStringLiteralsInJsxAttributes01.tsx (2 errors) ====
8+
9+
namespace JSX {
10+
interface IntrinsicElements {
11+
span: {};
12+
}
13+
}
14+
15+
const FooComponent = (props: { foo: "A" | "B" | "C" }) => <span>{props.foo}</span>;
16+
17+
<FooComponent foo={"A"} />;
18+
<FooComponent foo="A" />;
19+
20+
<FooComponent foo={"f"} />;
21+
~~~~~~~~~
22+
!!! error TS2322: Type '"f"' is not assignable to type '"A" | "B" | "C"'.
23+
!!! error TS2322: Type '"f"' is not assignable to type '"C"'.
24+
<FooComponent foo="f" />;
25+
~~~~~~~
26+
!!! error TS2322: Type '"f"' is not assignable to type '"A" | "B" | "C"'.
27+
!!! error TS2322: Type '"f"' is not assignable to type '"C"'.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//// [contextuallyTypedStringLiteralsInJsxAttributes01.tsx]
2+
3+
namespace JSX {
4+
interface IntrinsicElements {
5+
span: {};
6+
}
7+
}
8+
9+
const FooComponent = (props: { foo: "A" | "B" | "C" }) => <span>{props.foo}</span>;
10+
11+
<FooComponent foo={"A"} />;
12+
<FooComponent foo="A" />;
13+
14+
<FooComponent foo={"f"} />;
15+
<FooComponent foo="f" />;
16+
17+
//// [contextuallyTypedStringLiteralsInJsxAttributes01.jsx]
18+
var FooComponent = function (props) { return <span>{props.foo}</span>; };
19+
<FooComponent foo={"A"}/>;
20+
<FooComponent foo="A"/>;
21+
<FooComponent foo={"f"}/>;
22+
<FooComponent foo="f"/>;
23+
24+
25+
//// [contextuallyTypedStringLiteralsInJsxAttributes01.d.ts]
26+
declare namespace JSX {
27+
}
28+
declare const FooComponent: (props: {
29+
foo: "A" | "B" | "C";
30+
}) => any;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// @jsx: preserve
2+
// @declaration: true
3+
4+
namespace JSX {
5+
interface IntrinsicElements {
6+
span: {};
7+
}
8+
}
9+
10+
const FooComponent = (props: { foo: "A" | "B" | "C" }) => <span>{props.foo}</span>;
11+
12+
<FooComponent foo={"A"} />;
13+
<FooComponent foo="A" />;
14+
15+
<FooComponent foo={"f"} />;
16+
<FooComponent foo="f" />;

0 commit comments

Comments
 (0)