Merge pull request #6358 from RyanCavanaugh/fix6349

Change logic in identifying JSX React SFCs
This commit is contained in:
Ryan Cavanaugh 2016-01-12 15:40:52 -08:00
commit 5f024a3c9f
9 changed files with 191 additions and 18 deletions

View file

@ -8231,27 +8231,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 links.resolvedJsxType = 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;
}

View file

@ -1,6 +1,6 @@
//// [file.tsx]
declare module JSX {
interface Element { }
interface Element { something; }
interface IntrinsicElements { }
}

View file

@ -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 {

View file

@ -2,8 +2,9 @@
declare module JSX {
>JSX : any
interface Element { }
interface Element { something; }
>Element : Element
>something : any
interface IntrinsicElements { }
>IntrinsicElements : IntrinsicElements

View file

@ -0,0 +1,37 @@
//// [file.tsx]
import React = require('react');
const Foo = (props: any) => <div/>;
// Should be OK
const foo = <Foo />;
// Should be OK
var MainMenu: React.StatelessComponent<{}> = (props) => (<div>
<h3>Main Menu</h3>
</div>);
var App: React.StatelessComponent<{ children }> = ({children}) => (
<div >
<MainMenu/>
</div>
);
//// [file.jsx]
define(["require", "exports", 'react'], function (require, exports, React) {
"use strict";
var Foo = function (props) { return <div />; };
// Should be OK
var foo = <Foo />;
// Should be OK
var MainMenu = function (props) { return (<div>
<h3>Main Menu</h3>
</div>); };
var App = function (_a) {
var children = _a.children;
return (<div>
<MainMenu />
</div>);
};
});

View file

@ -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) => <div/>;
>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 />;
>foo : Symbol(foo, Decl(file.tsx, 5, 5))
>Foo : Symbol(Foo, Decl(file.tsx, 3, 5))
// Should be OK
var MainMenu: React.StatelessComponent<{}> = (props) => (<div>
>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))
<h3>Main Menu</h3>
>h3 : Symbol(JSX.IntrinsicElements.h3, Decl(react.d.ts, 939, 48))
>h3 : Symbol(JSX.IntrinsicElements.h3, Decl(react.d.ts, 939, 48))
</div>);
>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 >
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 927, 45))
<MainMenu/>
>MainMenu : Symbol(MainMenu, Decl(file.tsx, 9, 3))
</div>
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 927, 45))
);

View file

@ -0,0 +1,59 @@
=== tests/cases/conformance/jsx/file.tsx ===
import React = require('react');
>React : typeof React
const Foo = (props: any) => <div/>;
>Foo : (props: any) => JSX.Element
>(props: any) => <div/> : (props: any) => JSX.Element
>props : any
><div/> : JSX.Element
>div : any
// Should be OK
const foo = <Foo />;
>foo : JSX.Element
><Foo /> : JSX.Element
>Foo : (props: any) => JSX.Element
// Should be OK
var MainMenu: React.StatelessComponent<{}> = (props) => (<div>
>MainMenu : React.StatelessComponent<{}>
>React : any
>StatelessComponent : React.StatelessComponent<P>
>(props) => (<div> <h3>Main Menu</h3></div>) : (props: {}) => JSX.Element
>props : {}
>(<div> <h3>Main Menu</h3></div>) : JSX.Element
><div> <h3>Main Menu</h3></div> : JSX.Element
>div : any
<h3>Main Menu</h3>
><h3>Main Menu</h3> : JSX.Element
>h3 : any
>h3 : any
</div>);
>div : any
var App: React.StatelessComponent<{ children }> = ({children}) => (
>App : React.StatelessComponent<{ children: any; }>
>React : any
>StatelessComponent : React.StatelessComponent<P>
>children : any
>({children}) => ( <div > <MainMenu/> </div>) : ({children}: { children: any; }) => JSX.Element
>children : any
>( <div > <MainMenu/> </div>) : JSX.Element
<div >
><div > <MainMenu/> </div> : JSX.Element
>div : any
<MainMenu/>
><MainMenu/> : JSX.Element
>MainMenu : React.StatelessComponent<{}>
</div>
>div : any
);

View file

@ -1,7 +1,7 @@
//@filename: file.tsx
//@jsx: preserve
declare module JSX {
interface Element { }
interface Element { something; }
interface IntrinsicElements { }
}

View file

@ -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) => <div/>;
// Should be OK
const foo = <Foo />;
// Should be OK
var MainMenu: React.StatelessComponent<{}> = (props) => (<div>
<h3>Main Menu</h3>
</div>);
var App: React.StatelessComponent<{ children }> = ({children}) => (
<div >
<MainMenu/>
</div>
);