diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fb4af5d7298..a5b96c4a9a8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8248,27 +8248,31 @@ namespace ts { // Get the element instance type (the result of newing or invoking this tag) const elemInstanceType = getJsxElementInstanceType(node); - // Is this is a stateless function component? See if its single signature is - // assignable to the JSX Element Type - const callSignature = getSingleCallSignature(getTypeOfSymbol(sym)); - const callReturnType = callSignature && getReturnTypeOfSignature(callSignature); - let paramType = callReturnType && (callSignature.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(callSignature.parameters[0])); - if (callReturnType && isTypeAssignableTo(callReturnType, jsxElementType) && (paramType.flags & TypeFlags.ObjectType)) { - // Intersect in JSX.IntrinsicAttributes if it exists - const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes); - if (intrinsicAttributes !== unknownType) { - paramType = intersectTypes(intrinsicAttributes, paramType); + const elemClassType = getJsxGlobalElementClassType(); + + if (!elemClassType || !isTypeAssignableTo(elemInstanceType, elemClassType)) { + // Is this is a stateless function component? See if its single signature's return type is + // assignable to the JSX Element Type + const elemType = getTypeOfSymbol(sym); + const callSignatures = elemType && getSignaturesOfType(elemType, SignatureKind.Call); + const callSignature = callSignatures && callSignatures.length > 0 && callSignatures[0]; + const callReturnType = callSignature && getReturnTypeOfSignature(callSignature); + let paramType = callReturnType && (callSignature.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(callSignature.parameters[0])); + if (callReturnType && isTypeAssignableTo(callReturnType, jsxElementType)) { + // Intersect in JSX.IntrinsicAttributes if it exists + const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes); + if (intrinsicAttributes !== unknownType) { + paramType = intersectTypes(intrinsicAttributes, paramType); + } + return paramType; } - return paramType; } // Issue an error if this return type isn't assignable to JSX.ElementClass - const elemClassType = getJsxGlobalElementClassType(); if (elemClassType) { checkTypeRelatedTo(elemInstanceType, elemClassType, assignableRelation, node, Diagnostics.JSX_element_type_0_is_not_a_constructor_function_for_JSX_elements); } - if (isTypeAny(elemInstanceType)) { return links.resolvedJsxType = elemInstanceType; } diff --git a/tests/baselines/reference/tsxElementResolution9.js b/tests/baselines/reference/tsxElementResolution9.js index bc65bb0bb02..5ad47730e4a 100644 --- a/tests/baselines/reference/tsxElementResolution9.js +++ b/tests/baselines/reference/tsxElementResolution9.js @@ -1,6 +1,6 @@ //// [file.tsx] declare module JSX { - interface Element { } + interface Element { something; } interface IntrinsicElements { } } diff --git a/tests/baselines/reference/tsxElementResolution9.symbols b/tests/baselines/reference/tsxElementResolution9.symbols index e38c64d0847..0aec19a8094 100644 --- a/tests/baselines/reference/tsxElementResolution9.symbols +++ b/tests/baselines/reference/tsxElementResolution9.symbols @@ -2,11 +2,12 @@ declare module JSX { >JSX : Symbol(JSX, Decl(file.tsx, 0, 0)) - interface Element { } + interface Element { something; } >Element : Symbol(Element, Decl(file.tsx, 0, 20)) +>something : Symbol(something, Decl(file.tsx, 1, 20)) interface IntrinsicElements { } ->IntrinsicElements : Symbol(IntrinsicElements, Decl(file.tsx, 1, 22)) +>IntrinsicElements : Symbol(IntrinsicElements, Decl(file.tsx, 1, 33)) } interface Obj1 { diff --git a/tests/baselines/reference/tsxElementResolution9.types b/tests/baselines/reference/tsxElementResolution9.types index dd84e6f07b5..b138aa0509e 100644 --- a/tests/baselines/reference/tsxElementResolution9.types +++ b/tests/baselines/reference/tsxElementResolution9.types @@ -2,8 +2,9 @@ declare module JSX { >JSX : any - interface Element { } + interface Element { something; } >Element : Element +>something : any interface IntrinsicElements { } >IntrinsicElements : IntrinsicElements diff --git a/tests/baselines/reference/tsxStatelessFunctionComponents3.js b/tests/baselines/reference/tsxStatelessFunctionComponents3.js new file mode 100644 index 00000000000..d58586dd1ee --- /dev/null +++ b/tests/baselines/reference/tsxStatelessFunctionComponents3.js @@ -0,0 +1,37 @@ +//// [file.tsx] + +import React = require('react'); + +const Foo = (props: any) =>
; +// Should be OK +const foo = ; + + +// Should be OK +var MainMenu: React.StatelessComponent<{}> = (props) => (
+

Main Menu

+
); + +var App: React.StatelessComponent<{ children }> = ({children}) => ( +
+ +
+); + +//// [file.jsx] +define(["require", "exports", 'react'], function (require, exports, React) { + "use strict"; + var Foo = function (props) { return
; }; + // Should be OK + var foo = ; + // Should be OK + var MainMenu = function (props) { return (
+

Main Menu

+
); }; + var App = function (_a) { + var children = _a.children; + return (
+ +
); + }; +}); diff --git a/tests/baselines/reference/tsxStatelessFunctionComponents3.symbols b/tests/baselines/reference/tsxStatelessFunctionComponents3.symbols new file mode 100644 index 00000000000..4ce502c6a6a --- /dev/null +++ b/tests/baselines/reference/tsxStatelessFunctionComponents3.symbols @@ -0,0 +1,48 @@ +=== tests/cases/conformance/jsx/file.tsx === + +import React = require('react'); +>React : Symbol(React, Decl(file.tsx, 0, 0)) + +const Foo = (props: any) =>
; +>Foo : Symbol(Foo, Decl(file.tsx, 3, 5)) +>props : Symbol(props, Decl(file.tsx, 3, 13)) +>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 927, 45)) + +// Should be OK +const foo = ; +>foo : Symbol(foo, Decl(file.tsx, 5, 5)) +>Foo : Symbol(Foo, Decl(file.tsx, 3, 5)) + + +// Should be OK +var MainMenu: React.StatelessComponent<{}> = (props) => (
+>MainMenu : Symbol(MainMenu, Decl(file.tsx, 9, 3)) +>React : Symbol(React, Decl(file.tsx, 0, 0)) +>StatelessComponent : Symbol(React.StatelessComponent, Decl(react.d.ts, 139, 5)) +>props : Symbol(props, Decl(file.tsx, 9, 46)) +>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 927, 45)) + +

Main Menu

+>h3 : Symbol(JSX.IntrinsicElements.h3, Decl(react.d.ts, 939, 48)) +>h3 : Symbol(JSX.IntrinsicElements.h3, Decl(react.d.ts, 939, 48)) + +
); +>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 927, 45)) + +var App: React.StatelessComponent<{ children }> = ({children}) => ( +>App : Symbol(App, Decl(file.tsx, 13, 3)) +>React : Symbol(React, Decl(file.tsx, 0, 0)) +>StatelessComponent : Symbol(React.StatelessComponent, Decl(react.d.ts, 139, 5)) +>children : Symbol(children, Decl(file.tsx, 13, 35)) +>children : Symbol(children, Decl(file.tsx, 13, 52)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 927, 45)) + + +>MainMenu : Symbol(MainMenu, Decl(file.tsx, 9, 3)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 927, 45)) + +); diff --git a/tests/baselines/reference/tsxStatelessFunctionComponents3.types b/tests/baselines/reference/tsxStatelessFunctionComponents3.types new file mode 100644 index 00000000000..6531ff737ba --- /dev/null +++ b/tests/baselines/reference/tsxStatelessFunctionComponents3.types @@ -0,0 +1,59 @@ +=== tests/cases/conformance/jsx/file.tsx === + +import React = require('react'); +>React : typeof React + +const Foo = (props: any) =>
; +>Foo : (props: any) => JSX.Element +>(props: any) =>
: (props: any) => JSX.Element +>props : any +>
: JSX.Element +>div : any + +// Should be OK +const foo = ; +>foo : JSX.Element +> : JSX.Element +>Foo : (props: any) => JSX.Element + + +// Should be OK +var MainMenu: React.StatelessComponent<{}> = (props) => (
+>MainMenu : React.StatelessComponent<{}> +>React : any +>StatelessComponent : React.StatelessComponent

+>(props) => (

Main Menu

) : (props: {}) => JSX.Element +>props : {} +>(

Main Menu

) : JSX.Element +>

Main Menu

: JSX.Element +>div : any + +

Main Menu

+>

Main Menu

: JSX.Element +>h3 : any +>h3 : any + +
); +>div : any + +var App: React.StatelessComponent<{ children }> = ({children}) => ( +>App : React.StatelessComponent<{ children: any; }> +>React : any +>StatelessComponent : React.StatelessComponent

+>children : any +>({children}) => (

) : ({children}: { children: any; }) => JSX.Element +>children : any +>(
) : JSX.Element + +
+>
: JSX.Element +>div : any + + +> : JSX.Element +>MainMenu : React.StatelessComponent<{}> + +
+>div : any + +); diff --git a/tests/cases/conformance/jsx/tsxElementResolution9.tsx b/tests/cases/conformance/jsx/tsxElementResolution9.tsx index 7165f8277b3..4854484a225 100644 --- a/tests/cases/conformance/jsx/tsxElementResolution9.tsx +++ b/tests/cases/conformance/jsx/tsxElementResolution9.tsx @@ -1,7 +1,7 @@ //@filename: file.tsx //@jsx: preserve declare module JSX { - interface Element { } + interface Element { something; } interface IntrinsicElements { } } diff --git a/tests/cases/conformance/jsx/tsxStatelessFunctionComponents3.tsx b/tests/cases/conformance/jsx/tsxStatelessFunctionComponents3.tsx new file mode 100644 index 00000000000..48ce5fb5efb --- /dev/null +++ b/tests/cases/conformance/jsx/tsxStatelessFunctionComponents3.tsx @@ -0,0 +1,23 @@ +// @filename: file.tsx +// @jsx: preserve +// @module: amd +// @noLib: true +// @libFiles: react.d.ts,lib.d.ts + +import React = require('react'); + +const Foo = (props: any) =>
; +// Should be OK +const foo = ; + + +// Should be OK +var MainMenu: React.StatelessComponent<{}> = (props) => (
+

Main Menu

+
); + +var App: React.StatelessComponent<{ children }> = ({children}) => ( +
+ +
+); \ No newline at end of file