Skip to content

[Wip] Fix 11566 : allow null return in SFC #14045

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ namespace ts {
});

let jsxElementType: Type;
let jsxStatelessElementType: Type;
let _jsxNamespace: string;
let _jsxFactoryEntity: EntityName;

Expand All @@ -358,7 +359,8 @@ namespace ts {
ElementAttributesPropertyNameContainer: "ElementAttributesProperty",
Element: "Element",
IntrinsicAttributes: "IntrinsicAttributes",
IntrinsicClassAttributes: "IntrinsicClassAttributes"
IntrinsicClassAttributes: "IntrinsicClassAttributes",
StatelessElement: "StatelessElement"
};

const subtypeRelation = createMap<RelationComparisonResult>();
Expand Down Expand Up @@ -12197,13 +12199,13 @@ namespace ts {
function defaultTryGetJsxStatelessFunctionAttributesType(openingLikeElement: JsxOpeningLikeElement, elementType: Type, elemInstanceType: Type, elementClassType?: Type): Type {
Debug.assert(!(elementType.flags & TypeFlags.Union));
if (!elementClassType || !isTypeAssignableTo(elemInstanceType, elementClassType)) {
if (jsxElementType) {
if (jsxStatelessElementType) {
// We don't call getResolvedSignature here because we have already resolve the type of JSX Element.
const callSignature = getResolvedJsxStatelessFunctionSignature(openingLikeElement, elementType, /*candidatesOutArray*/ undefined);
if (callSignature !== unknownSignature) {
const callReturnType = callSignature && getReturnTypeOfSignature(callSignature);
let paramType = callReturnType && (callSignature.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(callSignature.parameters[0]));
if (callReturnType && isTypeAssignableTo(callReturnType, jsxElementType)) {
if (callReturnType && isTypeAssignableTo(callReturnType, jsxStatelessElementType)) {
// Intersect in JSX.IntrinsicAttributes if it exists
const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes);
if (intrinsicAttributes !== unknownType) {
Expand Down Expand Up @@ -12231,7 +12233,7 @@ namespace ts {
Debug.assert(!(elementType.flags & TypeFlags.Union));
if (!elementClassType || !isTypeAssignableTo(elemInstanceType, elementClassType)) {
// Is this is a stateless function component? See if its single signature's return type is assignable to the JSX Element Type
if (jsxElementType) {
if (jsxStatelessElementType) {
// We don't call getResolvedSignature because here we have already resolve the type of JSX Element.
const candidatesOutArray: Signature[] = [];
getResolvedJsxStatelessFunctionSignature(openingLikeElement, elementType, candidatesOutArray);
Expand All @@ -12240,7 +12242,7 @@ namespace ts {
for (const candidate of candidatesOutArray) {
const callReturnType = getReturnTypeOfSignature(candidate);
const paramType = callReturnType && (candidate.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(candidate.parameters[0]));
if (callReturnType && isTypeAssignableTo(callReturnType, jsxElementType)) {
if (callReturnType && isTypeAssignableTo(callReturnType, jsxStatelessElementType)) {
let shouldBeCandidate = true;
for (const attribute of openingLikeElement.attributes.properties) {
if (isJsxAttribute(attribute) &&
Expand Down Expand Up @@ -21280,6 +21282,7 @@ namespace ts {
globalRegExpType = getGlobalType("RegExp");

jsxElementType = getExportedTypeFromNamespace("JSX", JsxNames.Element);
jsxStatelessElementType = getExportedTypeFromNamespace("JSX", JsxNames.StatelessElement)
getGlobalClassDecoratorType = memoize(() => getGlobalType("ClassDecorator"));
getGlobalPropertyDecoratorType = memoize(() => getGlobalType("PropertyDecorator"));
getGlobalMethodDecoratorType = memoize(() => getGlobalType("MethodDecorator"));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
tests/cases/conformance/types/contextualTypes/jsxAttributes/contextuallyTypedStringLiteralsInJsxAttributes01.tsx(16,15): error TS2322: Type '{ foo: "f"; }' is not assignable to type '{ foo: "A" | "B" | "C"; }'.
tests/cases/conformance/types/contextualTypes/jsxAttributes/contextuallyTypedStringLiteralsInJsxAttributes01.tsx(17,15): error TS2322: Type '{ foo: "f"; }' is not assignable to type '{ foo: "A" | "B" | "C"; }'.
Types of property 'foo' are incompatible.
Type '"f"' is not assignable to type '"A" | "B" | "C"'.
tests/cases/conformance/types/contextualTypes/jsxAttributes/contextuallyTypedStringLiteralsInJsxAttributes01.tsx(17,15): error TS2322: Type '{ foo: "f"; }' is not assignable to type '{ foo: "A" | "B" | "C"; }'.
tests/cases/conformance/types/contextualTypes/jsxAttributes/contextuallyTypedStringLiteralsInJsxAttributes01.tsx(18,15): error TS2322: Type '{ foo: "f"; }' is not assignable to type '{ foo: "A" | "B" | "C"; }'.
Types of property 'foo' are incompatible.
Type '"f"' is not assignable to type '"A" | "B" | "C"'.


==== tests/cases/conformance/types/contextualTypes/jsxAttributes/contextuallyTypedStringLiteralsInJsxAttributes01.tsx (2 errors) ====

namespace JSX {
export type StatelessElement = JSX.Element | null;
export interface IntrinsicElements {
span: {};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//// [contextuallyTypedStringLiteralsInJsxAttributes01.tsx]

namespace JSX {
export type StatelessElement = JSX.Element | null;
export interface IntrinsicElements {
span: {};
}
Expand All @@ -27,6 +28,7 @@ var FooComponent = function (props) { return <span>{props.foo}</span>; };

//// [contextuallyTypedStringLiteralsInJsxAttributes01.d.ts]
declare namespace JSX {
type StatelessElement = JSX.Element | null;
interface IntrinsicElements {
span: {};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ tests/cases/conformance/jsx/file.tsx(16,28): error TS2322: Type '{ justRandomPro
==== tests/cases/conformance/jsx/react.d.ts (0 errors) ====

declare module JSX {
type StatelessElement = JSX.Element | null;
interface Element { }
interface IntrinsicElements {
div: any;
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/tsxAttributeResolution14.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//// [react.d.ts]

declare module JSX {
type StatelessElement = JSX.Element | null;
interface Element { }
interface IntrinsicElements {
div: any;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ class Poisoned extends React.Component<Prop, {}> {
>render : Symbol(Poisoned.render, Decl(file.tsx, 6, 50))

return <div>Hello</div>;
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ class Poisoned extends React.Component<Prop, {}> {
>render : Symbol(Poisoned.render, Decl(file.tsx, 6, 50))

return <div>Hello</div>;
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ class Poisoned extends React.Component<{}, {}> {
>render : Symbol(Poisoned.render, Decl(file.tsx, 3, 48))

return <div>Hello</div>;
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ class OverWriteAttr extends React.Component<Prop, {}> {
>render : Symbol(OverWriteAttr.render, Decl(file.tsx, 18, 55))

return <div>Hello</div>;
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ class Poisoned extends React.Component<PoisonedProp, {}> {
>render : Symbol(Poisoned.render, Decl(file.tsx, 8, 58))

return <div>Hello</div>;
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ class Poisoned extends React.Component<PoisonedProp, {}> {
>render : Symbol(Poisoned.render, Decl(file.tsx, 8, 58))

return <div>Hello</div>;
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
}
}

Expand Down Expand Up @@ -57,8 +57,8 @@ class EmptyProp extends React.Component<{}, {}> {
>render : Symbol(EmptyProp.render, Decl(file.tsx, 22, 49))

return <div>Default hi</div>;
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ class OverWriteAttr extends React.Component<Prop, {}> {
>render : Symbol(OverWriteAttr.render, Decl(file.tsx, 18, 55))

return <div>Hello</div>;
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ class Opt extends React.Component<OptionProp, {}> {
>render : Symbol(Opt.render, Decl(file.tsx, 8, 51))

return <div>Hello</div>;
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import React = require('react');
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, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))

// Should be OK
const foo = <Foo />;
Expand All @@ -20,14 +20,14 @@ var MainMenu: React.StatelessComponent<{}> = (props) => (<div>
>React : Symbol(React, Decl(file.tsx, 0, 0))
>StatelessComponent : Symbol(React.StatelessComponent, Decl(react.d.ts, 197, 40))
>props : Symbol(props, Decl(file.tsx, 9, 46))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))

<h3>Main Menu</h3>
>h3 : Symbol(JSX.IntrinsicElements.h3, Decl(react.d.ts, 2409, 48))
>h3 : Symbol(JSX.IntrinsicElements.h3, Decl(react.d.ts, 2409, 48))
>h3 : Symbol(JSX.IntrinsicElements.h3, Decl(react.d.ts, 2412, 48))
>h3 : Symbol(JSX.IntrinsicElements.h3, Decl(react.d.ts, 2412, 48))

</div>);
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))

var App: React.StatelessComponent<{ children }> = ({children}) => (
>App : Symbol(App, Decl(file.tsx, 13, 3))
Expand All @@ -37,12 +37,12 @@ var App: React.StatelessComponent<{ children }> = ({children}) => (
>children : Symbol(children, Decl(file.tsx, 13, 52))

<div >
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))

<MainMenu/>
>MainMenu : Symbol(MainMenu, Decl(file.tsx, 9, 3))

</div>
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))

);
8 changes: 4 additions & 4 deletions tests/baselines/reference/tsxUnionElementType1.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ function SFC1(prop: { x: number }) {
>x : Symbol(x, Decl(file.tsx, 3, 21))

return <div>hello</div>;
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))

};

Expand All @@ -20,8 +20,8 @@ function SFC2(prop: { x: boolean }) {
>x : Symbol(x, Decl(file.tsx, 7, 21))

return <h1>World </h1>;
>h1 : Symbol(JSX.IntrinsicElements.h1, Decl(react.d.ts, 2407, 47))
>h1 : Symbol(JSX.IntrinsicElements.h1, Decl(react.d.ts, 2407, 47))
>h1 : Symbol(JSX.IntrinsicElements.h1, Decl(react.d.ts, 2410, 47))
>h1 : Symbol(JSX.IntrinsicElements.h1, Decl(react.d.ts, 2410, 47))
}

var SFCComp = SFC1 || SFC2;
Expand Down
12 changes: 6 additions & 6 deletions tests/baselines/reference/tsxUnionElementType5.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ function EmptySFC1() {
>EmptySFC1 : Symbol(EmptySFC1, Decl(file.tsx, 1, 32))

return <div>hello</div>;
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
}

function EmptySFC2() {
>EmptySFC2 : Symbol(EmptySFC2, Decl(file.tsx, 5, 1))

return <div>Hello</div>;
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2400, 45))
}

function SFC2(prop: { x: boolean }) {
Expand All @@ -25,8 +25,8 @@ function SFC2(prop: { x: boolean }) {
>x : Symbol(x, Decl(file.tsx, 11, 21))

return <h1>World</h1>;
>h1 : Symbol(JSX.IntrinsicElements.h1, Decl(react.d.ts, 2407, 47))
>h1 : Symbol(JSX.IntrinsicElements.h1, Decl(react.d.ts, 2407, 47))
>h1 : Symbol(JSX.IntrinsicElements.h1, Decl(react.d.ts, 2410, 47))
>h1 : Symbol(JSX.IntrinsicElements.h1, Decl(react.d.ts, 2410, 47))
}

var EmptySFCComp = EmptySFC1 || EmptySFC2;
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/tsxUnionTypeComponent1.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ class MyComponent extends React.Component<ComponentProps, {}> {
<MyComponent AnyComponent={() => <button>test</button>}/>
>MyComponent : Symbol(MyComponent, Decl(file.tsx, 5, 1))
>AnyComponent : Symbol(AnyComponent, Decl(file.tsx, 15, 12))
>button : Symbol(JSX.IntrinsicElements.button, Decl(react.d.ts, 2383, 43))
>button : Symbol(JSX.IntrinsicElements.button, Decl(react.d.ts, 2383, 43))
>button : Symbol(JSX.IntrinsicElements.button, Decl(react.d.ts, 2386, 43))
>button : Symbol(JSX.IntrinsicElements.button, Decl(react.d.ts, 2386, 43))

// Component Class as Props
class MyButtonComponent extends React.Component<{},{}> {
Expand Down
1 change: 1 addition & 0 deletions tests/cases/conformance/jsx/tsxAttributeResolution14.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

//@filename: react.d.ts
declare module JSX {
type StatelessElement = JSX.Element | null;
interface Element { }
interface IntrinsicElements {
div: any;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// @declaration: true

namespace JSX {
export type StatelessElement = JSX.Element | null;
export interface IntrinsicElements {
span: {};
}
Expand Down
1 change: 1 addition & 0 deletions tests/cases/fourslash/tsxCompletion12.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// @noLib: true

//// declare module JSX {
//// type StatelessElement = JSX.Element | null;
//// interface Element { }
//// interface IntrinsicElements {
//// }
Expand Down
1 change: 1 addition & 0 deletions tests/cases/fourslash/tsxCompletion13.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// @noLib: true

//// declare module JSX {
//// type StatelessElement = JSX.Element | null;
//// interface Element { }
//// interface IntrinsicElements {
//// }
Expand Down
1 change: 1 addition & 0 deletions tests/cases/fourslash/tsxCompletionUnionElementType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// @noLib: true

//// declare module JSX {
//// type StatelessElement = JSX.Element | null;
//// interface Element { }
//// interface IntrinsicElements {
//// }
Expand Down
1 change: 1 addition & 0 deletions tests/cases/fourslash/tsxFindAllReferences10.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// @noLib: true

//// declare module JSX {
//// type StatelessElement = JSX.Element | null;
//// interface Element { }
//// interface IntrinsicElements {
//// }
Expand Down
1 change: 1 addition & 0 deletions tests/cases/fourslash/tsxFindAllReferences7.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// @noLib: true

//// declare module JSX {
//// type StatelessElement = JSX.Element | null;
//// interface Element { }
//// interface IntrinsicElements {
//// }
Expand Down
1 change: 1 addition & 0 deletions tests/cases/fourslash/tsxFindAllReferences9.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// @noLib: true

//// declare module JSX {
//// type StatelessElement = JSX.Element | null;
//// interface Element { }
//// interface IntrinsicElements {
//// }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// @noLib: true

//// declare module JSX {
//// type StatelessElement = JSX.Element | null;
//// interface Element { }
//// interface IntrinsicElements {
//// }
Expand Down
1 change: 1 addition & 0 deletions tests/cases/fourslash/tsxRename10.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// @noLib: true

//// declare module JSX {
//// type StatelessElement = JSX.Element | null;
//// interface Element { }
//// interface IntrinsicElements {
//// }
Expand Down
Loading