Skip to content

Commit 3735bb6

Browse files
authored
Merge pull request microsoft#21345 from Microsoft/fix-jsx-attribute-checking
Fix JSX attribute checking when spreading unions
2 parents 5dcb937 + ae65240 commit 3735bb6

File tree

5 files changed

+159
-1
lines changed

5 files changed

+159
-1
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15310,7 +15310,7 @@ namespace ts {
1531015310

1531115311
// If the targetAttributesType is an emptyObjectType, indicating that there is no property named 'props' on this instance type.
1531215312
// but there exists a sourceAttributesType, we need to explicitly give an error as normal assignability check allow excess properties and will pass.
15313-
if (targetAttributesType === emptyObjectType && (isTypeAny(sourceAttributesType) || (<ResolvedType>sourceAttributesType).properties.length > 0)) {
15313+
if (targetAttributesType === emptyObjectType && (isTypeAny(sourceAttributesType) || getPropertiesOfType(<ResolvedType>sourceAttributesType).length > 0)) {
1531415314
error(openingLikeElement, Diagnostics.JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, unescapeLeadingUnderscores(getJsxElementPropertiesName()));
1531515315
}
1531615316
else {
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//// [file.tsx]
2+
declare global {
3+
namespace JSX {
4+
interface Element {}
5+
interface ElementAttributesProperty { props: {} }
6+
}
7+
}
8+
declare var React: any;
9+
10+
export class Empty extends React.Component<{}, {}> {
11+
render() {
12+
return <div>Hello</div>;
13+
}
14+
}
15+
16+
declare const obj: { a: number | undefined } | undefined;
17+
18+
// OK
19+
let unionedSpread = <Empty {...obj} />;
20+
21+
22+
//// [file.jsx]
23+
"use strict";
24+
var __extends = (this && this.__extends) || (function () {
25+
var extendStatics = Object.setPrototypeOf ||
26+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
27+
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
28+
return function (d, b) {
29+
extendStatics(d, b);
30+
function __() { this.constructor = d; }
31+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
32+
};
33+
})();
34+
exports.__esModule = true;
35+
var Empty = /** @class */ (function (_super) {
36+
__extends(Empty, _super);
37+
function Empty() {
38+
return _super !== null && _super.apply(this, arguments) || this;
39+
}
40+
Empty.prototype.render = function () {
41+
return <div>Hello</div>;
42+
};
43+
return Empty;
44+
}(React.Component));
45+
exports.Empty = Empty;
46+
// OK
47+
var unionedSpread = <Empty {...obj}/>;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
=== tests/cases/conformance/jsx/file.tsx ===
2+
declare global {
3+
>global : Symbol(global, Decl(file.tsx, 0, 0))
4+
5+
namespace JSX {
6+
>JSX : Symbol(JSX, Decl(file.tsx, 0, 16))
7+
8+
interface Element {}
9+
>Element : Symbol(Element, Decl(file.tsx, 1, 19))
10+
11+
interface ElementAttributesProperty { props: {} }
12+
>ElementAttributesProperty : Symbol(ElementAttributesProperty, Decl(file.tsx, 2, 28))
13+
>props : Symbol(ElementAttributesProperty.props, Decl(file.tsx, 3, 45))
14+
}
15+
}
16+
declare var React: any;
17+
>React : Symbol(React, Decl(file.tsx, 6, 11))
18+
19+
export class Empty extends React.Component<{}, {}> {
20+
>Empty : Symbol(Empty, Decl(file.tsx, 6, 23))
21+
>React : Symbol(React, Decl(file.tsx, 6, 11))
22+
23+
render() {
24+
>render : Symbol(Empty.render, Decl(file.tsx, 8, 52))
25+
26+
return <div>Hello</div>;
27+
>div : Symbol(unknown)
28+
>div : Symbol(unknown)
29+
}
30+
}
31+
32+
declare const obj: { a: number | undefined } | undefined;
33+
>obj : Symbol(obj, Decl(file.tsx, 14, 13))
34+
>a : Symbol(a, Decl(file.tsx, 14, 20))
35+
36+
// OK
37+
let unionedSpread = <Empty {...obj} />;
38+
>unionedSpread : Symbol(unionedSpread, Decl(file.tsx, 17, 3))
39+
>Empty : Symbol(Empty, Decl(file.tsx, 6, 23))
40+
>obj : Symbol(obj, Decl(file.tsx, 14, 13))
41+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
=== tests/cases/conformance/jsx/file.tsx ===
2+
declare global {
3+
>global : any
4+
5+
namespace JSX {
6+
>JSX : any
7+
8+
interface Element {}
9+
>Element : Element
10+
11+
interface ElementAttributesProperty { props: {} }
12+
>ElementAttributesProperty : ElementAttributesProperty
13+
>props : {}
14+
}
15+
}
16+
declare var React: any;
17+
>React : any
18+
19+
export class Empty extends React.Component<{}, {}> {
20+
>Empty : Empty
21+
>React.Component : any
22+
>React : any
23+
>Component : any
24+
25+
render() {
26+
>render : () => JSX.Element
27+
28+
return <div>Hello</div>;
29+
><div>Hello</div> : JSX.Element
30+
>div : any
31+
>div : any
32+
}
33+
}
34+
35+
declare const obj: { a: number | undefined } | undefined;
36+
>obj : { a: number | undefined; } | undefined
37+
>a : number | undefined
38+
39+
// OK
40+
let unionedSpread = <Empty {...obj} />;
41+
>unionedSpread : JSX.Element
42+
><Empty {...obj} /> : JSX.Element
43+
>Empty : typeof Empty
44+
>obj : { a: number | undefined; } | undefined
45+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// @strictNullChecks: true
2+
// @filename: file.tsx
3+
// @jsx: preserve
4+
// @noLib: true
5+
// @skipLibCheck: true
6+
// @libFiles: lib.d.ts
7+
8+
declare global {
9+
namespace JSX {
10+
interface Element {}
11+
interface ElementAttributesProperty { props: {} }
12+
}
13+
}
14+
declare var React: any;
15+
16+
export class Empty extends React.Component<{}, {}> {
17+
render() {
18+
return <div>Hello</div>;
19+
}
20+
}
21+
22+
declare const obj: { a: number | undefined } | undefined;
23+
24+
// OK
25+
let unionedSpread = <Empty {...obj} />;

0 commit comments

Comments
 (0)