Skip to content

Commit f5d566e

Browse files
authored
Merge pull request microsoft#14152 from Microsoft/master-11566
[Master] Fix 11566 : SFC returns null
2 parents 990d2fa + 2e28c06 commit f5d566e

12 files changed

+283
-11
lines changed

src/compiler/checker.ts

+30-11
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,8 @@ namespace ts {
284284
let deferredGlobalAsyncIterableIteratorType: GenericType;
285285
let deferredGlobalTemplateStringsArrayType: ObjectType;
286286
let deferredJsxElementClassType: Type;
287+
let deferredJsxElementType: Type;
288+
let deferredJsxStatelessElementType: Type;
287289

288290
let deferredNodes: Node[];
289291
let deferredUnusedIdentifierNodes: Node[];
@@ -404,7 +406,6 @@ namespace ts {
404406
});
405407
const typeofType = createTypeofType();
406408

407-
let jsxElementType: Type;
408409
let _jsxNamespace: string;
409410
let _jsxFactoryEntity: EntityName;
410411

@@ -12462,12 +12463,12 @@ namespace ts {
1246212463
type.flags & TypeFlags.UnionOrIntersection && !forEach((<UnionOrIntersectionType>type).types, t => !isValidSpreadType(t)));
1246312464
}
1246412465

12465-
function checkJsxSelfClosingElement(node: JsxSelfClosingElement) {
12466+
function checkJsxSelfClosingElement(node: JsxSelfClosingElement): Type {
1246612467
checkJsxOpeningLikeElement(node);
12467-
return jsxElementType || anyType;
12468+
return getJsxGlobalElementType() || anyType;
1246812469
}
1246912470

12470-
function checkJsxElement(node: JsxElement) {
12471+
function checkJsxElement(node: JsxElement): Type {
1247112472
// Check attributes
1247212473
checkJsxOpeningLikeElement(node.openingElement);
1247312474

@@ -12494,7 +12495,7 @@ namespace ts {
1249412495
}
1249512496
}
1249612497

12497-
return jsxElementType || anyType;
12498+
return getJsxGlobalElementType() || anyType;
1249812499
}
1249912500

1250012501
/**
@@ -12735,13 +12736,14 @@ namespace ts {
1273512736
function defaultTryGetJsxStatelessFunctionAttributesType(openingLikeElement: JsxOpeningLikeElement, elementType: Type, elemInstanceType: Type, elementClassType?: Type): Type {
1273612737
Debug.assert(!(elementType.flags & TypeFlags.Union));
1273712738
if (!elementClassType || !isTypeAssignableTo(elemInstanceType, elementClassType)) {
12738-
if (jsxElementType) {
12739+
const jsxStatelessElementType = getJsxGlobalStatelessElementType();
12740+
if (jsxStatelessElementType) {
1273912741
// We don't call getResolvedSignature here because we have already resolve the type of JSX Element.
1274012742
const callSignature = getResolvedJsxStatelessFunctionSignature(openingLikeElement, elementType, /*candidatesOutArray*/ undefined);
1274112743
if (callSignature !== unknownSignature) {
1274212744
const callReturnType = callSignature && getReturnTypeOfSignature(callSignature);
1274312745
let paramType = callReturnType && (callSignature.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(callSignature.parameters[0]));
12744-
if (callReturnType && isTypeAssignableTo(callReturnType, jsxElementType)) {
12746+
if (callReturnType && isTypeAssignableTo(callReturnType, jsxStatelessElementType)) {
1274512747
// Intersect in JSX.IntrinsicAttributes if it exists
1274612748
const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes);
1274712749
if (intrinsicAttributes !== unknownType) {
@@ -12769,7 +12771,8 @@ namespace ts {
1276912771
Debug.assert(!(elementType.flags & TypeFlags.Union));
1277012772
if (!elementClassType || !isTypeAssignableTo(elemInstanceType, elementClassType)) {
1277112773
// Is this is a stateless function component? See if its single signature's return type is assignable to the JSX Element Type
12772-
if (jsxElementType) {
12774+
const jsxStatelessElementType = getJsxGlobalStatelessElementType();
12775+
if (jsxStatelessElementType) {
1277312776
// We don't call getResolvedSignature because here we have already resolve the type of JSX Element.
1277412777
const candidatesOutArray: Signature[] = [];
1277512778
getResolvedJsxStatelessFunctionSignature(openingLikeElement, elementType, candidatesOutArray);
@@ -12778,7 +12781,7 @@ namespace ts {
1277812781
for (const candidate of candidatesOutArray) {
1277912782
const callReturnType = getReturnTypeOfSignature(candidate);
1278012783
const paramType = callReturnType && (candidate.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(candidate.parameters[0]));
12781-
if (callReturnType && isTypeAssignableTo(callReturnType, jsxElementType)) {
12784+
if (callReturnType && isTypeAssignableTo(callReturnType, jsxStatelessElementType)) {
1278212785
let shouldBeCandidate = true;
1278312786
for (const attribute of openingLikeElement.attributes.properties) {
1278412787
if (isJsxAttribute(attribute) &&
@@ -13022,6 +13025,23 @@ namespace ts {
1302213025
return deferredJsxElementClassType;
1302313026
}
1302413027

13028+
function getJsxGlobalElementType(): Type {
13029+
if (!deferredJsxElementType) {
13030+
deferredJsxElementType = getExportedTypeFromNamespace(JsxNames.JSX, JsxNames.Element);
13031+
}
13032+
return deferredJsxElementType;
13033+
}
13034+
13035+
function getJsxGlobalStatelessElementType(): Type {
13036+
if (!deferredJsxStatelessElementType) {
13037+
const jsxElementType = getJsxGlobalElementType();
13038+
if (jsxElementType) {
13039+
deferredJsxStatelessElementType = getUnionType([jsxElementType, nullType]);
13040+
}
13041+
}
13042+
return deferredJsxStatelessElementType;
13043+
}
13044+
1302513045
/**
1302613046
* Returns all the properties of the Jsx.IntrinsicElements interface
1302713047
*/
@@ -13036,7 +13056,7 @@ namespace ts {
1303613056
error(errorNode, Diagnostics.Cannot_use_JSX_unless_the_jsx_flag_is_provided);
1303713057
}
1303813058

13039-
if (jsxElementType === undefined) {
13059+
if (getJsxGlobalElementType() === undefined) {
1304013060
if (noImplicitAny) {
1304113061
error(errorNode, Diagnostics.JSX_element_implicitly_has_type_any_because_the_global_type_JSX_Element_does_not_exist);
1304213062
}
@@ -22080,7 +22100,6 @@ namespace ts {
2208022100
globalNumberType = getGlobalType("Number", /*arity*/ 0, /*reportErrors*/ true);
2208122101
globalBooleanType = getGlobalType("Boolean", /*arity*/ 0, /*reportErrors*/ true);
2208222102
globalRegExpType = getGlobalType("RegExp", /*arity*/ 0, /*reportErrors*/ true);
22083-
jsxElementType = getExportedTypeFromNamespace("JSX", JsxNames.Element);
2208422103
anyArrayType = createArrayType(anyType);
2208522104
autoArrayType = createArrayType(autoType);
2208622105

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//// [file.tsx]
2+
3+
import React = require('react');
4+
5+
const Foo = (props: any) => null;
6+
7+
function Greet(x: {name?: string}) {
8+
return null;
9+
}
10+
11+
const foo = <Foo />;
12+
const G = <Greet />;
13+
14+
//// [file.jsx]
15+
define(["require", "exports", "react"], function (require, exports, React) {
16+
"use strict";
17+
exports.__esModule = true;
18+
var Foo = function (props) { return null; };
19+
function Greet(x) {
20+
return null;
21+
}
22+
var foo = <Foo />;
23+
var G = <Greet />;
24+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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) => null;
7+
>Foo : Symbol(Foo, Decl(file.tsx, 3, 5))
8+
>props : Symbol(props, Decl(file.tsx, 3, 13))
9+
10+
function Greet(x: {name?: string}) {
11+
>Greet : Symbol(Greet, Decl(file.tsx, 3, 33))
12+
>x : Symbol(x, Decl(file.tsx, 5, 15))
13+
>name : Symbol(name, Decl(file.tsx, 5, 19))
14+
15+
return null;
16+
}
17+
18+
const foo = <Foo />;
19+
>foo : Symbol(foo, Decl(file.tsx, 9, 5))
20+
>Foo : Symbol(Foo, Decl(file.tsx, 3, 5))
21+
22+
const G = <Greet />;
23+
>G : Symbol(G, Decl(file.tsx, 10, 5))
24+
>Greet : Symbol(Greet, Decl(file.tsx, 3, 33))
25+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
=== tests/cases/conformance/jsx/file.tsx ===
2+
3+
import React = require('react');
4+
>React : typeof React
5+
6+
const Foo = (props: any) => null;
7+
>Foo : (props: any) => any
8+
>(props: any) => null : (props: any) => any
9+
>props : any
10+
>null : null
11+
12+
function Greet(x: {name?: string}) {
13+
>Greet : (x: { name?: string; }) => any
14+
>x : { name?: string; }
15+
>name : string
16+
17+
return null;
18+
>null : null
19+
}
20+
21+
const foo = <Foo />;
22+
>foo : JSX.Element
23+
><Foo /> : JSX.Element
24+
>Foo : (props: any) => any
25+
26+
const G = <Greet />;
27+
>G : JSX.Element
28+
><Greet /> : JSX.Element
29+
>Greet : (x: { name?: string; }) => any
30+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//// [file.tsx]
2+
3+
import React = require('react');
4+
5+
const Foo = (props: any) => null;
6+
7+
function Greet(x: {name?: string}) {
8+
return null;
9+
}
10+
11+
const foo = <Foo />;
12+
const G = <Greet />;
13+
14+
//// [file.jsx]
15+
define(["require", "exports", "react"], function (require, exports, React) {
16+
"use strict";
17+
exports.__esModule = true;
18+
var Foo = function (props) { return null; };
19+
function Greet(x) {
20+
return null;
21+
}
22+
var foo = <Foo />;
23+
var G = <Greet />;
24+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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) => null;
7+
>Foo : Symbol(Foo, Decl(file.tsx, 3, 5))
8+
>props : Symbol(props, Decl(file.tsx, 3, 13))
9+
10+
function Greet(x: {name?: string}) {
11+
>Greet : Symbol(Greet, Decl(file.tsx, 3, 33))
12+
>x : Symbol(x, Decl(file.tsx, 5, 15))
13+
>name : Symbol(name, Decl(file.tsx, 5, 19))
14+
15+
return null;
16+
}
17+
18+
const foo = <Foo />;
19+
>foo : Symbol(foo, Decl(file.tsx, 9, 5))
20+
>Foo : Symbol(Foo, Decl(file.tsx, 3, 5))
21+
22+
const G = <Greet />;
23+
>G : Symbol(G, Decl(file.tsx, 10, 5))
24+
>Greet : Symbol(Greet, Decl(file.tsx, 3, 33))
25+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
=== tests/cases/conformance/jsx/file.tsx ===
2+
3+
import React = require('react');
4+
>React : typeof React
5+
6+
const Foo = (props: any) => null;
7+
>Foo : (props: any) => null
8+
>(props: any) => null : (props: any) => null
9+
>props : any
10+
>null : null
11+
12+
function Greet(x: {name?: string}) {
13+
>Greet : (x: { name?: string | undefined; }) => null
14+
>x : { name?: string | undefined; }
15+
>name : string | undefined
16+
17+
return null;
18+
>null : null
19+
}
20+
21+
const foo = <Foo />;
22+
>foo : JSX.Element
23+
><Foo /> : JSX.Element
24+
>Foo : (props: any) => null
25+
26+
const G = <Greet />;
27+
>G : JSX.Element
28+
><Greet /> : JSX.Element
29+
>Greet : (x: { name?: string | undefined; }) => null
30+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
tests/cases/conformance/jsx/file.tsx(10,13): error TS2605: JSX element type 'undefined' is not a constructor function for JSX elements.
2+
tests/cases/conformance/jsx/file.tsx(11,11): error TS2605: JSX element type 'undefined' is not a constructor function for JSX elements.
3+
4+
5+
==== tests/cases/conformance/jsx/file.tsx (2 errors) ====
6+
7+
import React = require('react');
8+
9+
const Foo = (props: any) => undefined;
10+
function Greet(x: {name?: string}) {
11+
return undefined;
12+
}
13+
14+
// Error
15+
const foo = <Foo />;
16+
~~~~~~~
17+
!!! error TS2605: JSX element type 'undefined' is not a constructor function for JSX elements.
18+
const G = <Greet />;
19+
~~~~~~~~~
20+
!!! error TS2605: JSX element type 'undefined' is not a constructor function for JSX elements.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//// [file.tsx]
2+
3+
import React = require('react');
4+
5+
const Foo = (props: any) => undefined;
6+
function Greet(x: {name?: string}) {
7+
return undefined;
8+
}
9+
10+
// Error
11+
const foo = <Foo />;
12+
const G = <Greet />;
13+
14+
//// [file.jsx]
15+
define(["require", "exports", "react"], function (require, exports, React) {
16+
"use strict";
17+
exports.__esModule = true;
18+
var Foo = function (props) { return undefined; };
19+
function Greet(x) {
20+
return undefined;
21+
}
22+
// Error
23+
var foo = <Foo />;
24+
var G = <Greet />;
25+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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) => null;
10+
11+
function Greet(x: {name?: string}) {
12+
return null;
13+
}
14+
15+
const foo = <Foo />;
16+
const G = <Greet />;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// @filename: file.tsx
2+
// @jsx: preserve
3+
// @module: amd
4+
// @noLib: true
5+
// @strictNullChecks: true
6+
// @libFiles: react.d.ts,lib.d.ts
7+
8+
import React = require('react');
9+
10+
const Foo = (props: any) => null;
11+
12+
function Greet(x: {name?: string}) {
13+
return null;
14+
}
15+
16+
const foo = <Foo />;
17+
const G = <Greet />;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// @filename: file.tsx
2+
// @jsx: preserve
3+
// @module: amd
4+
// @noLib: true
5+
// @strictNullChecks: true
6+
// @libFiles: react.d.ts,lib.d.ts
7+
8+
import React = require('react');
9+
10+
const Foo = (props: any) => undefined;
11+
function Greet(x: {name?: string}) {
12+
return undefined;
13+
}
14+
15+
// Error
16+
const foo = <Foo />;
17+
const G = <Greet />;

0 commit comments

Comments
 (0)