diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index eb0338c9f5..c2e798659d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7474,24 +7474,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 = expr.parent; - const attrsType = getJsxElementAttributesType(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); + } + else if (attribute.kind === SyntaxKind.JsxSpreadAttribute) { + return attrsType; } - if (expr.kind === SyntaxKind.JsxSpreadAttribute) { - return getJsxElementAttributesType(expr.parent); - } - - 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 @@ -7559,8 +7557,10 @@ namespace ts { case SyntaxKind.ParenthesizedExpression: return getContextualType(parent); case SyntaxKind.JsxExpression: + return getContextualType(parent); + case SyntaxKind.JsxAttribute: case SyntaxKind.JsxSpreadAttribute: - return getContextualTypeForJsxExpression(parent); + return getContextualTypeForJsxAttribute(parent); } return undefined; } diff --git a/tests/baselines/reference/contextuallyTypedStringLiteralsInJsxAttributes01.errors.txt b/tests/baselines/reference/contextuallyTypedStringLiteralsInJsxAttributes01.errors.txt new file mode 100644 index 0000000000..b5d4e01b67 --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedStringLiteralsInJsxAttributes01.errors.txt @@ -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" }) => {props.foo}; + + ; + ; + + ; + ~~~~~~~~~ +!!! error TS2322: Type '"f"' is not assignable to type '"A" | "B" | "C"'. +!!! error TS2322: Type '"f"' is not assignable to type '"C"'. + ; + ~~~~~~~ +!!! error TS2322: Type '"f"' is not assignable to type '"A" | "B" | "C"'. +!!! error TS2322: Type '"f"' is not assignable to type '"C"'. \ No newline at end of file diff --git a/tests/baselines/reference/contextuallyTypedStringLiteralsInJsxAttributes01.js b/tests/baselines/reference/contextuallyTypedStringLiteralsInJsxAttributes01.js new file mode 100644 index 0000000000..c21247c283 --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedStringLiteralsInJsxAttributes01.js @@ -0,0 +1,30 @@ +//// [contextuallyTypedStringLiteralsInJsxAttributes01.tsx] + +namespace JSX { + interface IntrinsicElements { + span: {}; + } +} + +const FooComponent = (props: { foo: "A" | "B" | "C" }) => {props.foo}; + +; +; + +; +; + +//// [contextuallyTypedStringLiteralsInJsxAttributes01.jsx] +var FooComponent = function (props) { return {props.foo}; }; +; +; +; +; + + +//// [contextuallyTypedStringLiteralsInJsxAttributes01.d.ts] +declare namespace JSX { +} +declare const FooComponent: (props: { + foo: "A" | "B" | "C"; +}) => any; diff --git a/tests/cases/conformance/types/contextualTypes/jsxAttributes/contextuallyTypedStringLiteralsInJsxAttributes01.tsx b/tests/cases/conformance/types/contextualTypes/jsxAttributes/contextuallyTypedStringLiteralsInJsxAttributes01.tsx new file mode 100644 index 0000000000..ce6f4b7ac2 --- /dev/null +++ b/tests/cases/conformance/types/contextualTypes/jsxAttributes/contextuallyTypedStringLiteralsInJsxAttributes01.tsx @@ -0,0 +1,16 @@ +// @jsx: preserve +// @declaration: true + +namespace JSX { + interface IntrinsicElements { + span: {}; + } +} + +const FooComponent = (props: { foo: "A" | "B" | "C" }) => {props.foo}; + +; +; + +; +; \ No newline at end of file