Skip to content

Commit 5f024a3

Browse files
committed
Merge pull request microsoft#6358 from RyanCavanaugh/fix6349
Change logic in identifying JSX React SFCs
2 parents 7a57b17 + cf8daff commit 5f024a3

9 files changed

Lines changed: 191 additions & 18 deletions

src/compiler/checker.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8231,27 +8231,31 @@ namespace ts {
82318231
// Get the element instance type (the result of newing or invoking this tag)
82328232
const elemInstanceType = getJsxElementInstanceType(node);
82338233

8234-
// Is this is a stateless function component? See if its single signature is
8235-
// assignable to the JSX Element Type
8236-
const callSignature = getSingleCallSignature(getTypeOfSymbol(sym));
8237-
const callReturnType = callSignature && getReturnTypeOfSignature(callSignature);
8238-
let paramType = callReturnType && (callSignature.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(callSignature.parameters[0]));
8239-
if (callReturnType && isTypeAssignableTo(callReturnType, jsxElementType) && (paramType.flags & TypeFlags.ObjectType)) {
8240-
// Intersect in JSX.IntrinsicAttributes if it exists
8241-
const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes);
8242-
if (intrinsicAttributes !== unknownType) {
8243-
paramType = intersectTypes(intrinsicAttributes, paramType);
8234+
const elemClassType = getJsxGlobalElementClassType();
8235+
8236+
if (!elemClassType || !isTypeAssignableTo(elemInstanceType, elemClassType)) {
8237+
// Is this is a stateless function component? See if its single signature's return type is
8238+
// assignable to the JSX Element Type
8239+
const elemType = getTypeOfSymbol(sym);
8240+
const callSignatures = elemType && getSignaturesOfType(elemType, SignatureKind.Call);
8241+
const callSignature = callSignatures && callSignatures.length > 0 && callSignatures[0];
8242+
const callReturnType = callSignature && getReturnTypeOfSignature(callSignature);
8243+
let paramType = callReturnType && (callSignature.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(callSignature.parameters[0]));
8244+
if (callReturnType && isTypeAssignableTo(callReturnType, jsxElementType)) {
8245+
// Intersect in JSX.IntrinsicAttributes if it exists
8246+
const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes);
8247+
if (intrinsicAttributes !== unknownType) {
8248+
paramType = intersectTypes(intrinsicAttributes, paramType);
8249+
}
8250+
return links.resolvedJsxType = paramType;
82448251
}
8245-
return paramType;
82468252
}
82478253

82488254
// Issue an error if this return type isn't assignable to JSX.ElementClass
8249-
const elemClassType = getJsxGlobalElementClassType();
82508255
if (elemClassType) {
82518256
checkTypeRelatedTo(elemInstanceType, elemClassType, assignableRelation, node, Diagnostics.JSX_element_type_0_is_not_a_constructor_function_for_JSX_elements);
82528257
}
82538258

8254-
82558259
if (isTypeAny(elemInstanceType)) {
82568260
return links.resolvedJsxType = elemInstanceType;
82578261
}

tests/baselines/reference/tsxElementResolution9.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//// [file.tsx]
22
declare module JSX {
3-
interface Element { }
3+
interface Element { something; }
44
interface IntrinsicElements { }
55
}
66

tests/baselines/reference/tsxElementResolution9.symbols

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
declare module JSX {
33
>JSX : Symbol(JSX, Decl(file.tsx, 0, 0))
44

5-
interface Element { }
5+
interface Element { something; }
66
>Element : Symbol(Element, Decl(file.tsx, 0, 20))
7+
>something : Symbol(something, Decl(file.tsx, 1, 20))
78

89
interface IntrinsicElements { }
9-
>IntrinsicElements : Symbol(IntrinsicElements, Decl(file.tsx, 1, 22))
10+
>IntrinsicElements : Symbol(IntrinsicElements, Decl(file.tsx, 1, 33))
1011
}
1112

1213
interface Obj1 {

tests/baselines/reference/tsxElementResolution9.types

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
declare module JSX {
33
>JSX : any
44

5-
interface Element { }
5+
interface Element { something; }
66
>Element : Element
7+
>something : any
78

89
interface IntrinsicElements { }
910
>IntrinsicElements : IntrinsicElements
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//// [file.tsx]
2+
3+
import React = require('react');
4+
5+
const Foo = (props: any) => <div/>;
6+
// Should be OK
7+
const foo = <Foo />;
8+
9+
10+
// Should be OK
11+
var MainMenu: React.StatelessComponent<{}> = (props) => (<div>
12+
<h3>Main Menu</h3>
13+
</div>);
14+
15+
var App: React.StatelessComponent<{ children }> = ({children}) => (
16+
<div >
17+
<MainMenu/>
18+
</div>
19+
);
20+
21+
//// [file.jsx]
22+
define(["require", "exports", 'react'], function (require, exports, React) {
23+
"use strict";
24+
var Foo = function (props) { return <div />; };
25+
// Should be OK
26+
var foo = <Foo />;
27+
// Should be OK
28+
var MainMenu = function (props) { return (<div>
29+
<h3>Main Menu</h3>
30+
</div>); };
31+
var App = function (_a) {
32+
var children = _a.children;
33+
return (<div>
34+
<MainMenu />
35+
</div>);
36+
};
37+
});
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
=== tests/cases/conformance/jsx/file.tsx ===
2+
3+
import React = require('react');
4+
>React : Symbol(React, Decl(file.tsx, 0, 0))
5+
6+
const Foo = (props: any) => <div/>;
7+
>Foo : Symbol(Foo, Decl(file.tsx, 3, 5))
8+
>props : Symbol(props, Decl(file.tsx, 3, 13))
9+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 927, 45))
10+
11+
// Should be OK
12+
const foo = <Foo />;
13+
>foo : Symbol(foo, Decl(file.tsx, 5, 5))
14+
>Foo : Symbol(Foo, Decl(file.tsx, 3, 5))
15+
16+
17+
// Should be OK
18+
var MainMenu: React.StatelessComponent<{}> = (props) => (<div>
19+
>MainMenu : Symbol(MainMenu, Decl(file.tsx, 9, 3))
20+
>React : Symbol(React, Decl(file.tsx, 0, 0))
21+
>StatelessComponent : Symbol(React.StatelessComponent, Decl(react.d.ts, 139, 5))
22+
>props : Symbol(props, Decl(file.tsx, 9, 46))
23+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 927, 45))
24+
25+
<h3>Main Menu</h3>
26+
>h3 : Symbol(JSX.IntrinsicElements.h3, Decl(react.d.ts, 939, 48))
27+
>h3 : Symbol(JSX.IntrinsicElements.h3, Decl(react.d.ts, 939, 48))
28+
29+
</div>);
30+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 927, 45))
31+
32+
var App: React.StatelessComponent<{ children }> = ({children}) => (
33+
>App : Symbol(App, Decl(file.tsx, 13, 3))
34+
>React : Symbol(React, Decl(file.tsx, 0, 0))
35+
>StatelessComponent : Symbol(React.StatelessComponent, Decl(react.d.ts, 139, 5))
36+
>children : Symbol(children, Decl(file.tsx, 13, 35))
37+
>children : Symbol(children, Decl(file.tsx, 13, 52))
38+
39+
<div >
40+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 927, 45))
41+
42+
<MainMenu/>
43+
>MainMenu : Symbol(MainMenu, Decl(file.tsx, 9, 3))
44+
45+
</div>
46+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 927, 45))
47+
48+
);
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
=== tests/cases/conformance/jsx/file.tsx ===
2+
3+
import React = require('react');
4+
>React : typeof React
5+
6+
const Foo = (props: any) => <div/>;
7+
>Foo : (props: any) => JSX.Element
8+
>(props: any) => <div/> : (props: any) => JSX.Element
9+
>props : any
10+
><div/> : JSX.Element
11+
>div : any
12+
13+
// Should be OK
14+
const foo = <Foo />;
15+
>foo : JSX.Element
16+
><Foo /> : JSX.Element
17+
>Foo : (props: any) => JSX.Element
18+
19+
20+
// Should be OK
21+
var MainMenu: React.StatelessComponent<{}> = (props) => (<div>
22+
>MainMenu : React.StatelessComponent<{}>
23+
>React : any
24+
>StatelessComponent : React.StatelessComponent<P>
25+
>(props) => (<div> <h3>Main Menu</h3></div>) : (props: {}) => JSX.Element
26+
>props : {}
27+
>(<div> <h3>Main Menu</h3></div>) : JSX.Element
28+
><div> <h3>Main Menu</h3></div> : JSX.Element
29+
>div : any
30+
31+
<h3>Main Menu</h3>
32+
><h3>Main Menu</h3> : JSX.Element
33+
>h3 : any
34+
>h3 : any
35+
36+
</div>);
37+
>div : any
38+
39+
var App: React.StatelessComponent<{ children }> = ({children}) => (
40+
>App : React.StatelessComponent<{ children: any; }>
41+
>React : any
42+
>StatelessComponent : React.StatelessComponent<P>
43+
>children : any
44+
>({children}) => ( <div > <MainMenu/> </div>) : ({children}: { children: any; }) => JSX.Element
45+
>children : any
46+
>( <div > <MainMenu/> </div>) : JSX.Element
47+
48+
<div >
49+
><div > <MainMenu/> </div> : JSX.Element
50+
>div : any
51+
52+
<MainMenu/>
53+
><MainMenu/> : JSX.Element
54+
>MainMenu : React.StatelessComponent<{}>
55+
56+
</div>
57+
>div : any
58+
59+
);

tests/cases/conformance/jsx/tsxElementResolution9.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//@filename: file.tsx
22
//@jsx: preserve
33
declare module JSX {
4-
interface Element { }
4+
interface Element { something; }
55
interface IntrinsicElements { }
66
}
77

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// @filename: file.tsx
2+
// @jsx: preserve
3+
// @module: amd
4+
// @noLib: true
5+
// @libFiles: react.d.ts,lib.d.ts
6+
7+
import React = require('react');
8+
9+
const Foo = (props: any) => <div/>;
10+
// Should be OK
11+
const foo = <Foo />;
12+
13+
14+
// Should be OK
15+
var MainMenu: React.StatelessComponent<{}> = (props) => (<div>
16+
<h3>Main Menu</h3>
17+
</div>);
18+
19+
var App: React.StatelessComponent<{ children }> = ({children}) => (
20+
<div >
21+
<MainMenu/>
22+
</div>
23+
);

0 commit comments

Comments
 (0)