Skip to content

Fix crash when the element instance type was undefined #3942

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

Merged
merged 6 commits into from
Jul 27, 2015
Merged
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
22 changes: 8 additions & 14 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4363,11 +4363,11 @@ namespace ts {
return getInferredType(context, i);
}
}
return t;
return t;
};

mapper.context = context;
return mapper;
return mapper;
}

function identityMapper(type: Type): Type {
Expand Down Expand Up @@ -7323,10 +7323,9 @@ namespace ts {
* For example, in the element <MyClass>, the element instance type is `MyClass` (not `typeof MyClass`).
*/
function getJsxElementInstanceType(node: JsxOpeningLikeElement) {
if (!(getNodeLinks(node).jsxFlags & JsxFlags.ClassElement)) {
// There is no such thing as an instance type for a non-class element
return undefined;
}
// There is no such thing as an instance type for a non-class element. This
// line shouldn't be hit.
Debug.assert(!!(getNodeLinks(node).jsxFlags & JsxFlags.ClassElement), 'Should not call getJsxElementInstanceType on non-class Element');

let classSymbol = getJsxElementTagSymbol(node);
if (classSymbol === unknownSymbol) {
Expand All @@ -7349,16 +7348,11 @@ namespace ts {
if (signatures.length === 0) {
// We found no signatures at all, which is an error
error(node.tagName, Diagnostics.JSX_element_type_0_does_not_have_any_construct_or_call_signatures, getTextOfNode(node.tagName));
return undefined;
return unknownType;
}
}

// Check that the constructor/factory returns an object type
let returnType = getUnionType(signatures.map(s => getReturnTypeOfSignature(s)));
if (!isTypeAny(returnType) && !(returnType.flags & TypeFlags.ObjectType)) {
error(node.tagName, Diagnostics.The_return_type_of_a_JSX_element_constructor_must_return_an_object_type);
return undefined;
}
let returnType = getUnionType(signatures.map(getReturnTypeOfSignature));

// Issue an error if this return type isn't assignable to JSX.ElementClass
let elemClassType = getJsxGlobalElementClassType();
Expand Down Expand Up @@ -7419,7 +7413,7 @@ namespace ts {
let elemInstanceType = getJsxElementInstanceType(node);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Under what situations does getJsxElementInstanceType return undefined?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When type of the specified element has no call or construct signatures

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RyanCavanaugh just tried the solution we discussed in #3960 and it fails to:

import * as React from 'react';

class C extends React.Component<any, any> {
    render() {
        return null
    }
}

type ReactCtor<P, S> = new() => React.Component<P, S> & { render(): React.ReactElement<P> };
let C1: ReactCtor<any, any> = C;
let a = <C1 />;

So maybe the error is not because of call or construct signature?

https://github.com/s-panferov/ts-jsx-issue

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RyanCavanaugh output from your branch:

➜  ts-jsx-issue git:(master) ✗ ../TypeScript/bin/tsc --target es6 --jsx react issue.tsx
issue.tsx(17,10): error TS2601: The return type of a JSX element constructor must return an object type.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RyanCavanaugh I found a working example

interface JsxClass<P, S> extends React.Component<P, S> {
    render(): React.ReactElement<P>
}

interface ReactCtor<P, S> {
    new(props: P): JsxClass<P, S>;
}

let C1: ReactCtor<any, any> = C;
let a = <C1 />;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hadn't tested with intersection types before. Updating.


if (isTypeAny(elemInstanceType)) {
return links.resolvedJsxType = anyType;
return links.resolvedJsxType = elemInstanceType;
}

let propsName = getJsxElementPropertiesName();
Expand Down
27 changes: 27 additions & 0 deletions tests/baselines/reference/jsxViaImport.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
tests/cases/compiler/consumer.tsx(5,17): error TS2604: JSX element type 'BaseComponent' does not have any construct or call signatures.


==== tests/cases/compiler/consumer.tsx (1 errors) ====
/// <reference path="component.d.ts" />
import BaseComponent = require('BaseComponent');
class TestComponent extends React.Component<any, {}> {
render() {
return <BaseComponent />;
~~~~~~~~~~~~~
!!! error TS2604: JSX element type 'BaseComponent' does not have any construct or call signatures.
}
}

==== tests/cases/compiler/component.d.ts (0 errors) ====

declare module JSX {
interface ElementAttributesProperty { props; }
}
declare module React {
class Component<T, U> { }
}
declare module "BaseComponent" {
var base: React.Component<any, {}>;
export = base;
}

43 changes: 43 additions & 0 deletions tests/baselines/reference/jsxViaImport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//// [tests/cases/compiler/jsxViaImport.tsx] ////

//// [component.d.ts]

declare module JSX {
interface ElementAttributesProperty { props; }
}
declare module React {
class Component<T, U> { }
}
declare module "BaseComponent" {
var base: React.Component<any, {}>;
export = base;
}

//// [consumer.tsx]
/// <reference path="component.d.ts" />
import BaseComponent = require('BaseComponent');
class TestComponent extends React.Component<any, {}> {
render() {
return <BaseComponent />;
}
}


//// [consumer.jsx]
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
/// <reference path="component.d.ts" />
var BaseComponent = require('BaseComponent');
var TestComponent = (function (_super) {
__extends(TestComponent, _super);
function TestComponent() {
_super.apply(this, arguments);
}
TestComponent.prototype.render = function () {
return <BaseComponent />;
};
return TestComponent;
})(React.Component);
8 changes: 1 addition & 7 deletions tests/baselines/reference/tsxElementResolution8.errors.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
tests/cases/conformance/jsx/tsxElementResolution8.tsx(8,2): error TS2604: JSX element type 'Div' does not have any construct or call signatures.
tests/cases/conformance/jsx/tsxElementResolution8.tsx(16,2): error TS2601: The return type of a JSX element constructor must return an object type.
tests/cases/conformance/jsx/tsxElementResolution8.tsx(29,2): error TS2601: The return type of a JSX element constructor must return an object type.
tests/cases/conformance/jsx/tsxElementResolution8.tsx(34,2): error TS2604: JSX element type 'Obj3' does not have any construct or call signatures.


==== tests/cases/conformance/jsx/tsxElementResolution8.tsx (4 errors) ====
==== tests/cases/conformance/jsx/tsxElementResolution8.tsx (2 errors) ====
declare module JSX {
interface Element { }
interface IntrinsicElements { }
Expand All @@ -23,8 +21,6 @@ tests/cases/conformance/jsx/tsxElementResolution8.tsx(34,2): error TS2604: JSX e
// Error
function Fnum(): number{ return 42; }
<Fnum />
~~~~
!!! error TS2601: The return type of a JSX element constructor must return an object type.

interface Obj1 {
new(): {};
Expand All @@ -38,8 +34,6 @@ tests/cases/conformance/jsx/tsxElementResolution8.tsx(34,2): error TS2604: JSX e
}
var Obj2: Obj2;
<Obj2 />; // Error
~~~~
!!! error TS2601: The return type of a JSX element constructor must return an object type.

interface Obj3 {
}
Expand Down
38 changes: 0 additions & 38 deletions tests/baselines/reference/tsxElementResolution9.errors.txt

This file was deleted.

67 changes: 67 additions & 0 deletions tests/baselines/reference/tsxElementResolution9.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
=== tests/cases/conformance/jsx/tsxElementResolution9.tsx ===
declare module JSX {
>JSX : Symbol(JSX, Decl(tsxElementResolution9.tsx, 0, 0))

interface Element { }
>Element : Symbol(Element, Decl(tsxElementResolution9.tsx, 0, 20))

interface IntrinsicElements { }
>IntrinsicElements : Symbol(IntrinsicElements, Decl(tsxElementResolution9.tsx, 1, 22))
}

interface Obj1 {
>Obj1 : Symbol(Obj1, Decl(tsxElementResolution9.tsx, 3, 1), Decl(tsxElementResolution9.tsx, 9, 3))

new(n: string): { x: number };
>n : Symbol(n, Decl(tsxElementResolution9.tsx, 6, 5))
>x : Symbol(x, Decl(tsxElementResolution9.tsx, 6, 18))

new(n: number): { y: string };
>n : Symbol(n, Decl(tsxElementResolution9.tsx, 7, 5))
>y : Symbol(y, Decl(tsxElementResolution9.tsx, 7, 18))
}
var Obj1: Obj1;
>Obj1 : Symbol(Obj1, Decl(tsxElementResolution9.tsx, 3, 1), Decl(tsxElementResolution9.tsx, 9, 3))
>Obj1 : Symbol(Obj1, Decl(tsxElementResolution9.tsx, 3, 1), Decl(tsxElementResolution9.tsx, 9, 3))

<Obj1 />; // Error, return type is not an object type
>Obj1 : Symbol(Obj1, Decl(tsxElementResolution9.tsx, 3, 1), Decl(tsxElementResolution9.tsx, 9, 3))

interface Obj2 {
>Obj2 : Symbol(Obj2, Decl(tsxElementResolution9.tsx, 10, 9), Decl(tsxElementResolution9.tsx, 16, 3))

(n: string): { x: number };
>n : Symbol(n, Decl(tsxElementResolution9.tsx, 13, 2))
>x : Symbol(x, Decl(tsxElementResolution9.tsx, 13, 15))

(n: number): { y: string };
>n : Symbol(n, Decl(tsxElementResolution9.tsx, 14, 2))
>y : Symbol(y, Decl(tsxElementResolution9.tsx, 14, 15))
}
var Obj2: Obj2;
>Obj2 : Symbol(Obj2, Decl(tsxElementResolution9.tsx, 10, 9), Decl(tsxElementResolution9.tsx, 16, 3))
>Obj2 : Symbol(Obj2, Decl(tsxElementResolution9.tsx, 10, 9), Decl(tsxElementResolution9.tsx, 16, 3))

<Obj2 />; // Error, return type is not an object type
>Obj2 : Symbol(Obj2, Decl(tsxElementResolution9.tsx, 10, 9), Decl(tsxElementResolution9.tsx, 16, 3))

interface Obj3 {
>Obj3 : Symbol(Obj3, Decl(tsxElementResolution9.tsx, 17, 9), Decl(tsxElementResolution9.tsx, 23, 3))

(n: string): { x: number };
>n : Symbol(n, Decl(tsxElementResolution9.tsx, 20, 2))
>x : Symbol(x, Decl(tsxElementResolution9.tsx, 20, 15))

(n: number): { x: number; y: string };
>n : Symbol(n, Decl(tsxElementResolution9.tsx, 21, 2))
>x : Symbol(x, Decl(tsxElementResolution9.tsx, 21, 15))
>y : Symbol(y, Decl(tsxElementResolution9.tsx, 21, 26))
}
var Obj3: Obj3;
>Obj3 : Symbol(Obj3, Decl(tsxElementResolution9.tsx, 17, 9), Decl(tsxElementResolution9.tsx, 23, 3))
>Obj3 : Symbol(Obj3, Decl(tsxElementResolution9.tsx, 17, 9), Decl(tsxElementResolution9.tsx, 23, 3))

<Obj3 x={42} />; // OK
>Obj3 : Symbol(Obj3, Decl(tsxElementResolution9.tsx, 17, 9), Decl(tsxElementResolution9.tsx, 23, 3))
>x : Symbol(unknown)

70 changes: 70 additions & 0 deletions tests/baselines/reference/tsxElementResolution9.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
=== tests/cases/conformance/jsx/tsxElementResolution9.tsx ===
declare module JSX {
>JSX : any

interface Element { }
>Element : Element

interface IntrinsicElements { }
>IntrinsicElements : IntrinsicElements
}

interface Obj1 {
>Obj1 : Obj1

new(n: string): { x: number };
>n : string
>x : number

new(n: number): { y: string };
>n : number
>y : string
}
var Obj1: Obj1;
>Obj1 : Obj1
>Obj1 : Obj1

<Obj1 />; // Error, return type is not an object type
><Obj1 /> : JSX.Element
>Obj1 : Obj1

interface Obj2 {
>Obj2 : Obj2

(n: string): { x: number };
>n : string
>x : number

(n: number): { y: string };
>n : number
>y : string
}
var Obj2: Obj2;
>Obj2 : Obj2
>Obj2 : Obj2

<Obj2 />; // Error, return type is not an object type
><Obj2 /> : JSX.Element
>Obj2 : Obj2

interface Obj3 {
>Obj3 : Obj3

(n: string): { x: number };
>n : string
>x : number

(n: number): { x: number; y: string };
>n : number
>x : number
>y : string
}
var Obj3: Obj3;
>Obj3 : Obj3
>Obj3 : Obj3

<Obj3 x={42} />; // OK
><Obj3 x={42} /> : JSX.Element
>Obj3 : Obj3
>x : any

23 changes: 23 additions & 0 deletions tests/cases/compiler/jsxViaImport.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//@jsx: preserve
//@module: commonjs

//@filename: component.d.ts
declare module JSX {
interface ElementAttributesProperty { props; }
}
declare module React {
class Component<T, U> { }
}
declare module "BaseComponent" {
var base: React.Component<any, {}>;
export = base;
}

//@filename: consumer.tsx
/// <reference path="component.d.ts" />
import BaseComponent = require('BaseComponent');
class TestComponent extends React.Component<any, {}> {
render() {
return <BaseComponent />;
}
}