Skip to content

Commit e90a328

Browse files
author
Kanchalai Tanglertsampan
committed
correctly handle the case when attributes type is empty object
1 parent 328f5cc commit e90a328

15 files changed

+433
-22
lines changed

src/compiler/checker.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7702,7 +7702,7 @@ namespace ts {
77027702
if (type.flags & TypeFlags.Object) {
77037703
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
77047704
if ((relation === assignableRelation || relation === comparableRelation) &&
7705-
(type === globalObjectType || isEmptyObjectType(resolved))) {
7705+
(type === globalObjectType || (!isComparingJsxAttributes && isEmptyObjectType(resolved)))) {
77067706
return true;
77077707
}
77087708
else if (resolved.stringIndexInfo || (resolved.numberIndexInfo && isNumericLiteralName(name))) {
@@ -12200,15 +12200,17 @@ namespace ts {
1220012200
if (jsxElementType) {
1220112201
// We don't call getResolvedSignature here because we have already resolve the type of JSX Element.
1220212202
const callSignature = getResolvedJsxStatelessFunctionSignature(openingLikeElement, elementType, /*candidatesOutArray*/ undefined);
12203-
const callReturnType = callSignature && getReturnTypeOfSignature(callSignature);
12204-
let paramType = callReturnType && (callSignature.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(callSignature.parameters[0]));
12205-
if (callReturnType && isTypeAssignableTo(callReturnType, jsxElementType)) {
12206-
// Intersect in JSX.IntrinsicAttributes if it exists
12207-
const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes);
12208-
if (intrinsicAttributes !== unknownType) {
12209-
paramType = intersectTypes(intrinsicAttributes, paramType);
12210-
}
12211-
return paramType;
12203+
if (callSignature !== unknownSignature) {
12204+
const callReturnType = callSignature && getReturnTypeOfSignature(callSignature);
12205+
let paramType = callReturnType && (callSignature.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(callSignature.parameters[0]));
12206+
if (callReturnType && isTypeAssignableTo(callReturnType, jsxElementType)) {
12207+
// Intersect in JSX.IntrinsicAttributes if it exists
12208+
const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes);
12209+
if (intrinsicAttributes !== unknownType) {
12210+
paramType = intersectTypes(intrinsicAttributes, paramType);
12211+
}
12212+
return paramType;
12213+
}
1221212214
}
1221312215
}
1221412216
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
tests/cases/conformance/jsx/file.tsx(12,21): error TS2322: Type '{ prop1: "hello"; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<BigGreeter> & {} & { children?: ReactNode; }'.
2+
Property 'prop1' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<BigGreeter> & {} & { children?: ReactNode; }'.
3+
4+
5+
==== tests/cases/conformance/jsx/file.tsx (1 errors) ====
6+
7+
import React = require('react');
8+
9+
class BigGreeter extends React.Component<{ }, {}> {
10+
render() {
11+
return <div>Default hi</div>;
12+
}
13+
greeting: string;
14+
}
15+
16+
// Error
17+
let a = <BigGreeter prop1="hello" />
18+
~~~~~~~~~~~~~
19+
!!! error TS2322: Type '{ prop1: "hello"; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<BigGreeter> & {} & { children?: ReactNode; }'.
20+
!!! error TS2322: Property 'prop1' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<BigGreeter> & {} & { children?: ReactNode; }'.
21+
22+
// OK
23+
let b = <BigGreeter ref={(input) => { this.textInput = input; }} />
24+
let c = <BigGreeter data-extra="hi" />
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//// [file.tsx]
2+
3+
import React = require('react');
4+
5+
class BigGreeter extends React.Component<{ }, {}> {
6+
render() {
7+
return <div>Default hi</div>;
8+
}
9+
greeting: string;
10+
}
11+
12+
// Error
13+
let a = <BigGreeter prop1="hello" />
14+
15+
// OK
16+
let b = <BigGreeter ref={(input) => { this.textInput = input; }} />
17+
let c = <BigGreeter data-extra="hi" />
18+
19+
//// [file.jsx]
20+
"use strict";
21+
var __extends = (this && this.__extends) || (function () {
22+
var extendStatics = Object.setPrototypeOf ||
23+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
24+
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
25+
return function (d, b) {
26+
extendStatics(d, b);
27+
function __() { this.constructor = d; }
28+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
29+
};
30+
})();
31+
var _this = this;
32+
var React = require("react");
33+
var BigGreeter = (function (_super) {
34+
__extends(BigGreeter, _super);
35+
function BigGreeter() {
36+
return _super !== null && _super.apply(this, arguments) || this;
37+
}
38+
BigGreeter.prototype.render = function () {
39+
return <div>Default hi</div>;
40+
};
41+
return BigGreeter;
42+
}(React.Component));
43+
// Error
44+
var a = <BigGreeter prop1="hello"/>;
45+
// OK
46+
var b = <BigGreeter ref={function (input) { _this.textInput = input; }}/>;
47+
var c = <BigGreeter data-extra="hi"/>;

tests/baselines/reference/tsxSpreadAttributesResolution4.js

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,21 @@ const obj: PoisonedProp = {
1919
};
2020

2121
// OK
22-
let p = <Poisoned {...obj} />;
22+
let p = <Poisoned {...obj} />;
23+
24+
class EmptyProp extends React.Component<{}, {}> {
25+
render() {
26+
return <div>Default hi</div>;
27+
}
28+
}
29+
30+
// OK
31+
let j: any;
32+
let e1 = <EmptyProp {...{}} />;
33+
let e2 = <EmptyProp {...j} />
34+
let e3 = <EmptyProp {...{ ref: (input) => { this.textInput = input; } }} />
35+
let e4 = <EmptyProp data-prop />
36+
let e5 = <EmptyProp {...{ "data-prop": true}} />
2337

2438
//// [file.jsx]
2539
"use strict";
@@ -33,6 +47,7 @@ var __extends = (this && this.__extends) || (function () {
3347
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
3448
};
3549
})();
50+
var _this = this;
3651
var React = require("react");
3752
var Poisoned = (function (_super) {
3853
__extends(Poisoned, _super);
@@ -50,3 +65,20 @@ var obj = {
5065
};
5166
// OK
5267
var p = <Poisoned {...obj}/>;
68+
var EmptyProp = (function (_super) {
69+
__extends(EmptyProp, _super);
70+
function EmptyProp() {
71+
return _super !== null && _super.apply(this, arguments) || this;
72+
}
73+
EmptyProp.prototype.render = function () {
74+
return <div>Default hi</div>;
75+
};
76+
return EmptyProp;
77+
}(React.Component));
78+
// OK
79+
var j;
80+
var e1 = <EmptyProp {...{}}/>;
81+
var e2 = <EmptyProp {...j}/>;
82+
var e3 = <EmptyProp {...{ ref: function (input) { _this.textInput = input; } }}/>;
83+
var e4 = <EmptyProp data-prop/>;
84+
var e5 = <EmptyProp {...{ "data-prop": true }}/>;

tests/baselines/reference/tsxSpreadAttributesResolution4.symbols

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,47 @@ let p = <Poisoned {...obj} />;
4747
>Poisoned : Symbol(Poisoned, Decl(file.tsx, 6, 1))
4848
>obj : Symbol(obj, Decl(file.tsx, 14, 5))
4949

50+
class EmptyProp extends React.Component<{}, {}> {
51+
>EmptyProp : Symbol(EmptyProp, Decl(file.tsx, 20, 30))
52+
>React.Component : Symbol(React.Component, Decl(react.d.ts, 158, 55))
53+
>React : Symbol(React, Decl(file.tsx, 0, 0))
54+
>Component : Symbol(React.Component, Decl(react.d.ts, 158, 55))
55+
56+
render() {
57+
>render : Symbol(EmptyProp.render, Decl(file.tsx, 22, 49))
58+
59+
return <div>Default hi</div>;
60+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
61+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2397, 45))
62+
}
63+
}
64+
65+
// OK
66+
let j: any;
67+
>j : Symbol(j, Decl(file.tsx, 29, 3))
68+
69+
let e1 = <EmptyProp {...{}} />;
70+
>e1 : Symbol(e1, Decl(file.tsx, 30, 3))
71+
>EmptyProp : Symbol(EmptyProp, Decl(file.tsx, 20, 30))
72+
73+
let e2 = <EmptyProp {...j} />
74+
>e2 : Symbol(e2, Decl(file.tsx, 31, 3))
75+
>EmptyProp : Symbol(EmptyProp, Decl(file.tsx, 20, 30))
76+
>j : Symbol(j, Decl(file.tsx, 29, 3))
77+
78+
let e3 = <EmptyProp {...{ ref: (input) => { this.textInput = input; } }} />
79+
>e3 : Symbol(e3, Decl(file.tsx, 32, 3))
80+
>EmptyProp : Symbol(EmptyProp, Decl(file.tsx, 20, 30))
81+
>ref : Symbol(ref, Decl(file.tsx, 32, 25))
82+
>input : Symbol(input, Decl(file.tsx, 32, 32))
83+
>input : Symbol(input, Decl(file.tsx, 32, 32))
84+
85+
let e4 = <EmptyProp data-prop />
86+
>e4 : Symbol(e4, Decl(file.tsx, 33, 3))
87+
>EmptyProp : Symbol(EmptyProp, Decl(file.tsx, 20, 30))
88+
>data-prop : Symbol(data-prop, Decl(file.tsx, 33, 19))
89+
90+
let e5 = <EmptyProp {...{ "data-prop": true}} />
91+
>e5 : Symbol(e5, Decl(file.tsx, 34, 3))
92+
>EmptyProp : Symbol(EmptyProp, Decl(file.tsx, 20, 30))
93+

tests/baselines/reference/tsxSpreadAttributesResolution4.types

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,62 @@ let p = <Poisoned {...obj} />;
5252
>Poisoned : typeof Poisoned
5353
>obj : PoisonedProp
5454

55+
class EmptyProp extends React.Component<{}, {}> {
56+
>EmptyProp : EmptyProp
57+
>React.Component : React.Component<{}, {}>
58+
>React : typeof React
59+
>Component : typeof React.Component
60+
61+
render() {
62+
>render : () => JSX.Element
63+
64+
return <div>Default hi</div>;
65+
><div>Default hi</div> : JSX.Element
66+
>div : any
67+
>div : any
68+
}
69+
}
70+
71+
// OK
72+
let j: any;
73+
>j : any
74+
75+
let e1 = <EmptyProp {...{}} />;
76+
>e1 : JSX.Element
77+
><EmptyProp {...{}} /> : JSX.Element
78+
>EmptyProp : typeof EmptyProp
79+
>{} : {}
80+
81+
let e2 = <EmptyProp {...j} />
82+
>e2 : JSX.Element
83+
><EmptyProp {...j} /> : JSX.Element
84+
>EmptyProp : typeof EmptyProp
85+
>j : any
86+
87+
let e3 = <EmptyProp {...{ ref: (input) => { this.textInput = input; } }} />
88+
>e3 : JSX.Element
89+
><EmptyProp {...{ ref: (input) => { this.textInput = input; } }} /> : JSX.Element
90+
>EmptyProp : typeof EmptyProp
91+
>{ ref: (input) => { this.textInput = input; } } : { ref: (input: EmptyProp) => void; }
92+
>ref : (input: EmptyProp) => void
93+
>(input) => { this.textInput = input; } : (input: EmptyProp) => void
94+
>input : EmptyProp
95+
>this.textInput = input : EmptyProp
96+
>this.textInput : any
97+
>this : any
98+
>textInput : any
99+
>input : EmptyProp
100+
101+
let e4 = <EmptyProp data-prop />
102+
>e4 : JSX.Element
103+
><EmptyProp data-prop /> : JSX.Element
104+
>EmptyProp : typeof EmptyProp
105+
>data-prop : true
106+
107+
let e5 = <EmptyProp {...{ "data-prop": true}} />
108+
>e5 : JSX.Element
109+
><EmptyProp {...{ "data-prop": true}} /> : JSX.Element
110+
>EmptyProp : typeof EmptyProp
111+
>{ "data-prop": true} : { "data-prop": boolean; }
112+
>true : true
113+

tests/baselines/reference/tsxSpreadAttributesResolution5.errors.txt

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ tests/cases/conformance/jsx/file.tsx(21,19): error TS2322: Type '{ x: string; y:
22
Type '{ x: string; y: number; }' is not assignable to type 'PoisonedProp'.
33
Types of property 'y' are incompatible.
44
Type 'number' is not assignable to type '2'.
5+
tests/cases/conformance/jsx/file.tsx(34,20): error TS2322: Type '{ prop1: boolean; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<EmptyProp> & {} & { children?: ReactNode; }'.
6+
Property 'prop1' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<EmptyProp> & {} & { children?: ReactNode; }'.
57

68

7-
==== tests/cases/conformance/jsx/file.tsx (1 errors) ====
9+
==== tests/cases/conformance/jsx/file.tsx (2 errors) ====
810

911
import React = require('react');
1012

@@ -30,4 +32,20 @@ tests/cases/conformance/jsx/file.tsx(21,19): error TS2322: Type '{ x: string; y:
3032
!!! error TS2322: Type '{ x: string; y: number; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<Poisoned> & PoisonedProp & { children?: ReactNode; }'.
3133
!!! error TS2322: Type '{ x: string; y: number; }' is not assignable to type 'PoisonedProp'.
3234
!!! error TS2322: Types of property 'y' are incompatible.
33-
!!! error TS2322: Type 'number' is not assignable to type '2'.
35+
!!! error TS2322: Type 'number' is not assignable to type '2'.
36+
37+
class EmptyProp extends React.Component<{}, {}> {
38+
render() {
39+
return <div>Default hi</div>;
40+
}
41+
greeting: string;
42+
}
43+
44+
let o = {
45+
prop1: false
46+
}
47+
// Error
48+
let e = <EmptyProp {...o} />;
49+
~~~~~~
50+
!!! error TS2322: Type '{ prop1: boolean; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<EmptyProp> & {} & { children?: ReactNode; }'.
51+
!!! error TS2322: Property 'prop1' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<EmptyProp> & {} & { children?: ReactNode; }'.

tests/baselines/reference/tsxSpreadAttributesResolution5.js

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,20 @@ let obj = {
1919
};
2020

2121
// Error as "obj" has type { x: string; y: number }
22-
let p = <Poisoned {...obj} />;
22+
let p = <Poisoned {...obj} />;
23+
24+
class EmptyProp extends React.Component<{}, {}> {
25+
render() {
26+
return <div>Default hi</div>;
27+
}
28+
greeting: string;
29+
}
30+
31+
let o = {
32+
prop1: false
33+
}
34+
// Error
35+
let e = <EmptyProp {...o} />;
2336

2437
//// [file.jsx]
2538
"use strict";
@@ -50,3 +63,18 @@ var obj = {
5063
};
5164
// Error as "obj" has type { x: string; y: number }
5265
var p = <Poisoned {...obj}/>;
66+
var EmptyProp = (function (_super) {
67+
__extends(EmptyProp, _super);
68+
function EmptyProp() {
69+
return _super !== null && _super.apply(this, arguments) || this;
70+
}
71+
EmptyProp.prototype.render = function () {
72+
return <div>Default hi</div>;
73+
};
74+
return EmptyProp;
75+
}(React.Component));
76+
var o = {
77+
prop1: false
78+
};
79+
// Error
80+
var e = <EmptyProp {...o}/>;

0 commit comments

Comments
 (0)