diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 16bfd7fa7e253..2089b76171026 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15159,7 +15159,7 @@ namespace ts { * element is not a class element, or the class element type cannot be determined, returns 'undefined'. * For example, in the element , the element instance type is `MyClass` (not `typeof MyClass`). */ - function getJsxElementInstanceType(node: JsxOpeningLikeElement, valueType: Type, sourceAttributesType: Type) { + function getJsxElementInstanceType(node: JsxOpeningLikeElement, valueType: Type, sourceAttributesType: Type | undefined) { Debug.assert(!(valueType.flags & TypeFlags.Union)); if (isTypeAny(valueType)) { // Short-circuit if the class tag is using an element type 'any' @@ -15178,20 +15178,27 @@ namespace ts { } } - const instantiatedSignatures = []; - for (const signature of signatures) { - if (signature.typeParameters) { - const isJavascript = isInJavaScriptFile(node); - const inferenceContext = createInferenceContext(signature, /*flags*/ isJavascript ? InferenceFlags.AnyDefault : 0); - const typeArguments = inferJsxTypeArguments(signature, sourceAttributesType, inferenceContext); - instantiatedSignatures.push(getSignatureInstantiation(signature, typeArguments, isJavascript)); - } - else { - instantiatedSignatures.push(signature); + if (sourceAttributesType) { + // Instantiate in context of source type + const instantiatedSignatures = []; + for (const signature of signatures) { + if (signature.typeParameters) { + const isJavascript = isInJavaScriptFile(node); + const inferenceContext = createInferenceContext(signature, /*flags*/ isJavascript ? InferenceFlags.AnyDefault : 0); + const typeArguments = inferJsxTypeArguments(signature, sourceAttributesType, inferenceContext); + instantiatedSignatures.push(getSignatureInstantiation(signature, typeArguments, isJavascript)); + } + else { + instantiatedSignatures.push(signature); + } } - } - return getUnionType(map(instantiatedSignatures, getReturnTypeOfSignature), UnionReduction.Subtype); + return getUnionType(map(instantiatedSignatures, getReturnTypeOfSignature), UnionReduction.Subtype); + } + else { + // Do not instantiate if no source type is provided - type parameters and their constraints will be used by contextual typing + return getUnionType(map(signatures, getReturnTypeOfSignature), UnionReduction.Subtype); + } } /** @@ -15415,7 +15422,7 @@ namespace ts { } // Get the element instance type (the result of newing or invoking this tag) - const elemInstanceType = getJsxElementInstanceType(openingLikeElement, elementType, sourceAttributesType || emptyObjectType); + const elemInstanceType = getJsxElementInstanceType(openingLikeElement, elementType, sourceAttributesType); // If we should include all stateless attributes type, then get all attributes type from all stateless function signature. // Otherwise get only attributes type from the signature picked by choose-overload logic. diff --git a/tests/baselines/reference/jsxHasLiteralType.js b/tests/baselines/reference/jsxHasLiteralType.js new file mode 100644 index 0000000000000..491f58eebb1bb --- /dev/null +++ b/tests/baselines/reference/jsxHasLiteralType.js @@ -0,0 +1,32 @@ +//// [jsxHasLiteralType.tsx] +import * as React from "react"; + +interface Props { + x?: "a" | "b"; +} +class MyComponent

extends React.Component {} +const m = + + +//// [jsxHasLiteralType.js] +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +exports.__esModule = true; +var React = require("react"); +var MyComponent = /** @class */ (function (_super) { + __extends(MyComponent, _super); + function MyComponent() { + return _super !== null && _super.apply(this, arguments) || this; + } + return MyComponent; +}(React.Component)); +var m = React.createElement(MyComponent, { x: "a" }); diff --git a/tests/baselines/reference/jsxHasLiteralType.symbols b/tests/baselines/reference/jsxHasLiteralType.symbols new file mode 100644 index 0000000000000..a7106f2699e72 --- /dev/null +++ b/tests/baselines/reference/jsxHasLiteralType.symbols @@ -0,0 +1,25 @@ +=== tests/cases/compiler/jsxHasLiteralType.tsx === +import * as React from "react"; +>React : Symbol(React, Decl(jsxHasLiteralType.tsx, 0, 6)) + +interface Props { +>Props : Symbol(Props, Decl(jsxHasLiteralType.tsx, 0, 31)) + + x?: "a" | "b"; +>x : Symbol(Props.x, Decl(jsxHasLiteralType.tsx, 2, 17)) +} +class MyComponent

extends React.Component {} +>MyComponent : Symbol(MyComponent, Decl(jsxHasLiteralType.tsx, 4, 1)) +>P : Symbol(P, Decl(jsxHasLiteralType.tsx, 5, 18)) +>Props : Symbol(Props, Decl(jsxHasLiteralType.tsx, 0, 31)) +>Props : Symbol(Props, Decl(jsxHasLiteralType.tsx, 0, 31)) +>React.Component : Symbol(React.Component, Decl(react.d.ts, 158, 55), Decl(react.d.ts, 161, 66)) +>React : Symbol(React, Decl(jsxHasLiteralType.tsx, 0, 6)) +>Component : Symbol(React.Component, Decl(react.d.ts, 158, 55), Decl(react.d.ts, 161, 66)) +>P : Symbol(P, Decl(jsxHasLiteralType.tsx, 5, 18)) + +const m = +>m : Symbol(m, Decl(jsxHasLiteralType.tsx, 6, 5)) +>MyComponent : Symbol(MyComponent, Decl(jsxHasLiteralType.tsx, 4, 1)) +>x : Symbol(x, Decl(jsxHasLiteralType.tsx, 6, 22)) + diff --git a/tests/baselines/reference/jsxHasLiteralType.types b/tests/baselines/reference/jsxHasLiteralType.types new file mode 100644 index 0000000000000..61120edcea679 --- /dev/null +++ b/tests/baselines/reference/jsxHasLiteralType.types @@ -0,0 +1,26 @@ +=== tests/cases/compiler/jsxHasLiteralType.tsx === +import * as React from "react"; +>React : typeof React + +interface Props { +>Props : Props + + x?: "a" | "b"; +>x : "a" | "b" | undefined +} +class MyComponent

extends React.Component {} +>MyComponent : MyComponent

+>P : P +>Props : Props +>Props : Props +>React.Component : React.Component +>React : typeof React +>Component : typeof React.Component +>P : P + +const m = +>m : JSX.Element +> : JSX.Element +>MyComponent : typeof MyComponent +>x : "a" + diff --git a/tests/cases/compiler/jsxHasLiteralType.tsx b/tests/cases/compiler/jsxHasLiteralType.tsx new file mode 100644 index 0000000000000..17132fdad40fd --- /dev/null +++ b/tests/cases/compiler/jsxHasLiteralType.tsx @@ -0,0 +1,11 @@ +// @strictNullChecks: true +// @jsx: react +// @skipLibCheck: true +// @libFiles: lib.d.ts,react.d.ts +import * as React from "react"; + +interface Props { + x?: "a" | "b"; +} +class MyComponent

extends React.Component {} +const m =