Skip to content

Contextually type JSX string literal attributes #6232

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7464,24 +7464,22 @@ namespace ts {
return node === conditional.whenTrue || node === conditional.whenFalse ? getContextualType(conditional) : undefined;
}

function getContextualTypeForJsxExpression(expr: JsxExpression | JsxSpreadAttribute): Type {
// Contextual type only applies to JSX expressions that are in attribute assignments (not in 'Children' positions)
if (expr.parent.kind === SyntaxKind.JsxAttribute) {
const attrib = <JsxAttribute>expr.parent;
const attrsType = getJsxElementAttributesType(<JsxOpeningLikeElement>attrib.parent);
function getContextualTypeForJsxAttribute(attribute: JsxAttribute | JsxSpreadAttribute) {
const kind = attribute.kind;
const jsxElement = attribute.parent as JsxOpeningLikeElement;
const attrsType = getJsxElementAttributesType(jsxElement);

if (attribute.kind === SyntaxKind.JsxAttribute) {
if (!attrsType || isTypeAny(attrsType)) {
return undefined;
}
else {
return getTypeOfPropertyOfType(attrsType, attrib.name.text);
}
return getTypeOfPropertyOfType(attrsType, (attribute as JsxAttribute).name.text);
}

if (expr.kind === SyntaxKind.JsxSpreadAttribute) {
return getJsxElementAttributesType(<JsxOpeningLikeElement>expr.parent);
else if (attribute.kind === SyntaxKind.JsxSpreadAttribute) {
return attrsType;
}

return undefined;
Debug.fail(`Expected JsxAttribute or JsxSpreadAttribute, got ts.SyntaxKind[${kind}]`);
}

// Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily
Expand Down Expand Up @@ -7549,8 +7547,10 @@ namespace ts {
case SyntaxKind.ParenthesizedExpression:
return getContextualType(<ParenthesizedExpression>parent);
case SyntaxKind.JsxExpression:
return getContextualType(<JsxExpression>parent);
case SyntaxKind.JsxAttribute:
case SyntaxKind.JsxSpreadAttribute:
return getContextualTypeForJsxExpression(<JsxExpression>parent);
return getContextualTypeForJsxAttribute(<JsxAttribute | JsxSpreadAttribute>parent);
}
return undefined;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
tests/cases/conformance/types/contextualTypes/jsxAttributes/contextuallyTypedStringLiteralsInJsxAttributes01.tsx(13,15): error TS2322: Type '"f"' is not assignable to type '"A" | "B" | "C"'.
Type '"f"' is not assignable to type '"C"'.
tests/cases/conformance/types/contextualTypes/jsxAttributes/contextuallyTypedStringLiteralsInJsxAttributes01.tsx(14,15): error TS2322: Type '"f"' is not assignable to type '"A" | "B" | "C"'.
Type '"f"' is not assignable to type '"C"'.


==== tests/cases/conformance/types/contextualTypes/jsxAttributes/contextuallyTypedStringLiteralsInJsxAttributes01.tsx (2 errors) ====

namespace JSX {
interface IntrinsicElements {
span: {};
}
}

const FooComponent = (props: { foo: "A" | "B" | "C" }) => <span>{props.foo}</span>;

<FooComponent foo={"A"} />;
<FooComponent foo="A" />;

<FooComponent foo={"f"} />;
~~~~~~~~~
!!! error TS2322: Type '"f"' is not assignable to type '"A" | "B" | "C"'.
!!! error TS2322: Type '"f"' is not assignable to type '"C"'.
<FooComponent foo="f" />;
~~~~~~~
!!! error TS2322: Type '"f"' is not assignable to type '"A" | "B" | "C"'.
!!! error TS2322: Type '"f"' is not assignable to type '"C"'.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//// [contextuallyTypedStringLiteralsInJsxAttributes01.tsx]

namespace JSX {
interface IntrinsicElements {
span: {};
}
}

const FooComponent = (props: { foo: "A" | "B" | "C" }) => <span>{props.foo}</span>;

<FooComponent foo={"A"} />;
<FooComponent foo="A" />;

<FooComponent foo={"f"} />;
<FooComponent foo="f" />;

//// [contextuallyTypedStringLiteralsInJsxAttributes01.jsx]
var FooComponent = function (props) { return <span>{props.foo}</span>; };
<FooComponent foo={"A"}/>;
<FooComponent foo="A"/>;
<FooComponent foo={"f"}/>;
<FooComponent foo="f"/>;


//// [contextuallyTypedStringLiteralsInJsxAttributes01.d.ts]
declare namespace JSX {
}
declare const FooComponent: (props: {
foo: "A" | "B" | "C";
}) => any;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// @jsx: preserve
// @declaration: true

namespace JSX {
interface IntrinsicElements {
span: {};
}
}

const FooComponent = (props: { foo: "A" | "B" | "C" }) => <span>{props.foo}</span>;

<FooComponent foo={"A"} />;
<FooComponent foo="A" />;

<FooComponent foo={"f"} />;
<FooComponent foo="f" />;