Skip to content

Commit 4b2ef23

Browse files
committed
add support for jsx attributes
1 parent c286684 commit 4b2ef23

File tree

6 files changed

+372
-2
lines changed

6 files changed

+372
-2
lines changed

src/compiler/checker.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -13613,7 +13613,7 @@ namespace ts {
1361313613
function propertyRelatedTo(source: Type, target: Type, sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean): Ternary {
1361413614
let assignmentVariance;
1361513615

13616-
if (strictAssignment) {
13616+
if (strictAssignment && sourceProp.valueDeclaration) {
1361713617
const {aliasSymbol, symbol} = getTypeOfSymbol(targetProp);
1361813618
const propEscapedName = (aliasSymbol && symbolName(aliasSymbol)) || (symbol && symbolName(symbol));
1361913619
const targetPropIsReadonly = contains(["Readonly", "ReadonlyArray", "ReadonlyMap", "ReadonlySet"], propEscapedName);
@@ -13636,6 +13636,15 @@ namespace ts {
1363613636
// PropertyDeclarations are properties declared within classes
1363713637
assignmentVariance = !targetPropIsReadonly && targetPropIsArray ? VarianceFlags.Invariant : undefined;
1363813638
}
13639+
else if (isJsxAttribute(sourceProp.valueDeclaration)) {
13640+
const {initializer} = sourceProp.valueDeclaration;
13641+
if (initializer && isJsxExpression(initializer)) {
13642+
const {expression} = initializer;
13643+
if (expression && isIdentifier(expression)) {
13644+
assignmentVariance = VarianceFlags.Invariant;
13645+
}
13646+
}
13647+
}
1363913648
}
1364013649

1364113650
const sourcePropFlags = getDeclarationModifierFlagsFromSymbol(sourceProp);
@@ -21135,7 +21144,6 @@ namespace ts {
2113521144
const paramType = getEffectiveFirstArgumentForJsxSignature(signature, node);
2113621145
const attributesType = checkExpressionWithContextualType(node.attributes, paramType, /*inferenceContext*/ undefined, checkMode);
2113721146

21138-
// TODO: set assignmentVariance to Invariant for props that identifiers
2113921147
return checkTypeRelatedToAndOptionallyElaborate(attributesType, paramType, relation, reportErrors ? node.tagName : undefined, node.attributes);
2114021148
}
2114121149

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
tests/cases/compiler/strictAssignment5.tsx(20,6): error TS2322: Type '{ animals: Cat[]; }' is not assignable to type 'Readonly<Props>'.
2+
Types of property 'animals' are incompatible.
3+
Type 'Cat[]' is not assignable to type 'Animal[]'. Covariant assignment of aliases to non-readonly targets are unsafe.
4+
5+
6+
==== tests/cases/compiler/strictAssignment5.tsx (1 errors) ====
7+
/// <reference path="/.lib/react16.d.ts" />
8+
import React from "react";
9+
10+
module StrictAssignment5 {
11+
class Animal {}
12+
class Cat { purr() {} }
13+
class Dog { bark() {} }
14+
15+
type Props = {
16+
animals: Animal[],
17+
};
18+
19+
class Foo extends React.Component<Props> {
20+
render() {
21+
return "foo";
22+
}
23+
}
24+
25+
const cats: Cat[] = [new Cat];
26+
<Foo animals={cats} />; // error
27+
~~~
28+
!!! error TS2322: Type '{ animals: Cat[]; }' is not assignable to type 'Readonly<Props>'.
29+
!!! error TS2322: Types of property 'animals' are incompatible.
30+
!!! error TS2322: Type 'Cat[]' is not assignable to type 'Animal[]'. Covariant assignment of aliases to non-readonly targets are unsafe.
31+
!!! related TS2728 tests/cases/compiler/strictAssignment5.tsx:6:17: 'purr' is declared here.
32+
<Foo animals={[new Cat]} />; // okay
33+
34+
type ReadonlyProps = {
35+
animals: ReadonlyArray<Animal>,
36+
};
37+
38+
class Bar extends React.Component<ReadonlyProps> {
39+
render() {
40+
return "foo";
41+
}
42+
}
43+
44+
<Bar animals={cats} />; // okay
45+
}
46+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//// [strictAssignment5.tsx]
2+
/// <reference path="/.lib/react16.d.ts" />
3+
import React from "react";
4+
5+
module StrictAssignment5 {
6+
class Animal {}
7+
class Cat { purr() {} }
8+
class Dog { bark() {} }
9+
10+
type Props = {
11+
animals: Animal[],
12+
};
13+
14+
class Foo extends React.Component<Props> {
15+
render() {
16+
return "foo";
17+
}
18+
}
19+
20+
const cats: Cat[] = [new Cat];
21+
<Foo animals={cats} />; // error
22+
<Foo animals={[new Cat]} />; // okay
23+
24+
type ReadonlyProps = {
25+
animals: ReadonlyArray<Animal>,
26+
};
27+
28+
class Bar extends React.Component<ReadonlyProps> {
29+
render() {
30+
return "foo";
31+
}
32+
}
33+
34+
<Bar animals={cats} />; // okay
35+
}
36+
37+
38+
//// [strictAssignment5.js]
39+
"use strict";
40+
var __extends = (this && this.__extends) || (function () {
41+
var extendStatics = function (d, b) {
42+
extendStatics = Object.setPrototypeOf ||
43+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
44+
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
45+
return extendStatics(d, b);
46+
};
47+
return function (d, b) {
48+
extendStatics(d, b);
49+
function __() { this.constructor = d; }
50+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
51+
};
52+
})();
53+
var __importDefault = (this && this.__importDefault) || function (mod) {
54+
return (mod && mod.__esModule) ? mod : { "default": mod };
55+
};
56+
exports.__esModule = true;
57+
/// <reference path="react16.d.ts" />
58+
var react_1 = __importDefault(require("react"));
59+
var StrictAssignment5;
60+
(function (StrictAssignment5) {
61+
var Animal = /** @class */ (function () {
62+
function Animal() {
63+
}
64+
return Animal;
65+
}());
66+
var Cat = /** @class */ (function () {
67+
function Cat() {
68+
}
69+
Cat.prototype.purr = function () { };
70+
return Cat;
71+
}());
72+
var Dog = /** @class */ (function () {
73+
function Dog() {
74+
}
75+
Dog.prototype.bark = function () { };
76+
return Dog;
77+
}());
78+
var Foo = /** @class */ (function (_super) {
79+
__extends(Foo, _super);
80+
function Foo() {
81+
return _super !== null && _super.apply(this, arguments) || this;
82+
}
83+
Foo.prototype.render = function () {
84+
return "foo";
85+
};
86+
return Foo;
87+
}(react_1["default"].Component));
88+
var cats = [new Cat];
89+
react_1["default"].createElement(Foo, { animals: cats }); // error
90+
react_1["default"].createElement(Foo, { animals: [new Cat] }); // okay
91+
var Bar = /** @class */ (function (_super) {
92+
__extends(Bar, _super);
93+
function Bar() {
94+
return _super !== null && _super.apply(this, arguments) || this;
95+
}
96+
Bar.prototype.render = function () {
97+
return "foo";
98+
};
99+
return Bar;
100+
}(react_1["default"].Component));
101+
react_1["default"].createElement(Bar, { animals: cats }); // okay
102+
})(StrictAssignment5 || (StrictAssignment5 = {}));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
=== tests/cases/compiler/strictAssignment5.tsx ===
2+
/// <reference path="react16.d.ts" />
3+
import React from "react";
4+
>React : Symbol(React, Decl(strictAssignment5.tsx, 1, 6))
5+
6+
module StrictAssignment5 {
7+
>StrictAssignment5 : Symbol(StrictAssignment5, Decl(strictAssignment5.tsx, 1, 26))
8+
9+
class Animal {}
10+
>Animal : Symbol(Animal, Decl(strictAssignment5.tsx, 3, 26))
11+
12+
class Cat { purr() {} }
13+
>Cat : Symbol(Cat, Decl(strictAssignment5.tsx, 4, 19))
14+
>purr : Symbol(Cat.purr, Decl(strictAssignment5.tsx, 5, 15))
15+
16+
class Dog { bark() {} }
17+
>Dog : Symbol(Dog, Decl(strictAssignment5.tsx, 5, 27))
18+
>bark : Symbol(Dog.bark, Decl(strictAssignment5.tsx, 6, 15))
19+
20+
type Props = {
21+
>Props : Symbol(Props, Decl(strictAssignment5.tsx, 6, 27))
22+
23+
animals: Animal[],
24+
>animals : Symbol(animals, Decl(strictAssignment5.tsx, 8, 18))
25+
>Animal : Symbol(Animal, Decl(strictAssignment5.tsx, 3, 26))
26+
27+
};
28+
29+
class Foo extends React.Component<Props> {
30+
>Foo : Symbol(Foo, Decl(strictAssignment5.tsx, 10, 6))
31+
>React.Component : Symbol(React.Component, Decl(react16.d.ts, 345, 54), Decl(react16.d.ts, 349, 94))
32+
>React : Symbol(React, Decl(strictAssignment5.tsx, 1, 6))
33+
>Component : Symbol(React.Component, Decl(react16.d.ts, 345, 54), Decl(react16.d.ts, 349, 94))
34+
>Props : Symbol(Props, Decl(strictAssignment5.tsx, 6, 27))
35+
36+
render() {
37+
>render : Symbol(Foo.render, Decl(strictAssignment5.tsx, 12, 46))
38+
39+
return "foo";
40+
}
41+
}
42+
43+
const cats: Cat[] = [new Cat];
44+
>cats : Symbol(cats, Decl(strictAssignment5.tsx, 18, 9))
45+
>Cat : Symbol(Cat, Decl(strictAssignment5.tsx, 4, 19))
46+
>Cat : Symbol(Cat, Decl(strictAssignment5.tsx, 4, 19))
47+
48+
<Foo animals={cats} />; // error
49+
>Foo : Symbol(Foo, Decl(strictAssignment5.tsx, 10, 6))
50+
>animals : Symbol(animals, Decl(strictAssignment5.tsx, 19, 8))
51+
>cats : Symbol(cats, Decl(strictAssignment5.tsx, 18, 9))
52+
53+
<Foo animals={[new Cat]} />; // okay
54+
>Foo : Symbol(Foo, Decl(strictAssignment5.tsx, 10, 6))
55+
>animals : Symbol(animals, Decl(strictAssignment5.tsx, 20, 8))
56+
>Cat : Symbol(Cat, Decl(strictAssignment5.tsx, 4, 19))
57+
58+
type ReadonlyProps = {
59+
>ReadonlyProps : Symbol(ReadonlyProps, Decl(strictAssignment5.tsx, 20, 32))
60+
61+
animals: ReadonlyArray<Animal>,
62+
>animals : Symbol(animals, Decl(strictAssignment5.tsx, 22, 26))
63+
>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --))
64+
>Animal : Symbol(Animal, Decl(strictAssignment5.tsx, 3, 26))
65+
66+
};
67+
68+
class Bar extends React.Component<ReadonlyProps> {
69+
>Bar : Symbol(Bar, Decl(strictAssignment5.tsx, 24, 6))
70+
>React.Component : Symbol(React.Component, Decl(react16.d.ts, 345, 54), Decl(react16.d.ts, 349, 94))
71+
>React : Symbol(React, Decl(strictAssignment5.tsx, 1, 6))
72+
>Component : Symbol(React.Component, Decl(react16.d.ts, 345, 54), Decl(react16.d.ts, 349, 94))
73+
>ReadonlyProps : Symbol(ReadonlyProps, Decl(strictAssignment5.tsx, 20, 32))
74+
75+
render() {
76+
>render : Symbol(Bar.render, Decl(strictAssignment5.tsx, 26, 54))
77+
78+
return "foo";
79+
}
80+
}
81+
82+
<Bar animals={cats} />; // okay
83+
>Bar : Symbol(Bar, Decl(strictAssignment5.tsx, 24, 6))
84+
>animals : Symbol(animals, Decl(strictAssignment5.tsx, 32, 8))
85+
>cats : Symbol(cats, Decl(strictAssignment5.tsx, 18, 9))
86+
}
87+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
=== tests/cases/compiler/strictAssignment5.tsx ===
2+
/// <reference path="react16.d.ts" />
3+
import React from "react";
4+
>React : typeof React
5+
6+
module StrictAssignment5 {
7+
>StrictAssignment5 : typeof StrictAssignment5
8+
9+
class Animal {}
10+
>Animal : Animal
11+
12+
class Cat { purr() {} }
13+
>Cat : Cat
14+
>purr : () => void
15+
16+
class Dog { bark() {} }
17+
>Dog : Dog
18+
>bark : () => void
19+
20+
type Props = {
21+
>Props : { animals: Animal[]; }
22+
23+
animals: Animal[],
24+
>animals : Animal[]
25+
26+
};
27+
28+
class Foo extends React.Component<Props> {
29+
>Foo : Foo
30+
>React.Component : React.Component<{ animals: Animal[]; }, {}, any>
31+
>React : typeof React
32+
>Component : typeof React.Component
33+
34+
render() {
35+
>render : () => string
36+
37+
return "foo";
38+
>"foo" : "foo"
39+
}
40+
}
41+
42+
const cats: Cat[] = [new Cat];
43+
>cats : Cat[]
44+
>[new Cat] : Cat[]
45+
>new Cat : Cat
46+
>Cat : typeof Cat
47+
48+
<Foo animals={cats} />; // error
49+
><Foo animals={cats} /> : JSX.Element
50+
>Foo : typeof Foo
51+
>animals : Cat[]
52+
>cats : Cat[]
53+
54+
<Foo animals={[new Cat]} />; // okay
55+
><Foo animals={[new Cat]} /> : JSX.Element
56+
>Foo : typeof Foo
57+
>animals : Cat[]
58+
>[new Cat] : Cat[]
59+
>new Cat : Cat
60+
>Cat : typeof Cat
61+
62+
type ReadonlyProps = {
63+
>ReadonlyProps : { animals: readonly Animal[]; }
64+
65+
animals: ReadonlyArray<Animal>,
66+
>animals : readonly Animal[]
67+
68+
};
69+
70+
class Bar extends React.Component<ReadonlyProps> {
71+
>Bar : Bar
72+
>React.Component : React.Component<{ animals: readonly Animal[]; }, {}, any>
73+
>React : typeof React
74+
>Component : typeof React.Component
75+
76+
render() {
77+
>render : () => string
78+
79+
return "foo";
80+
>"foo" : "foo"
81+
}
82+
}
83+
84+
<Bar animals={cats} />; // okay
85+
><Bar animals={cats} /> : JSX.Element
86+
>Bar : typeof Bar
87+
>animals : Cat[]
88+
>cats : Cat[]
89+
}
90+

0 commit comments

Comments
 (0)