Skip to content

Commit

Permalink
add support for jsx attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinbarabash committed Jun 21, 2019
1 parent c286684 commit 2f4bd00
Show file tree
Hide file tree
Showing 5 changed files with 326 additions and 2 deletions.
12 changes: 10 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13613,7 +13613,7 @@ namespace ts {
function propertyRelatedTo(source: Type, target: Type, sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean): Ternary {
let assignmentVariance;

if (strictAssignment) {
if (strictAssignment && sourceProp.valueDeclaration) {
const {aliasSymbol, symbol} = getTypeOfSymbol(targetProp);
const propEscapedName = (aliasSymbol && symbolName(aliasSymbol)) || (symbol && symbolName(symbol));
const targetPropIsReadonly = contains(["Readonly", "ReadonlyArray", "ReadonlyMap", "ReadonlySet"], propEscapedName);
Expand All @@ -13636,6 +13636,15 @@ namespace ts {
// PropertyDeclarations are properties declared within classes
assignmentVariance = !targetPropIsReadonly && targetPropIsArray ? VarianceFlags.Invariant : undefined;
}
else if (isJsxAttribute(sourceProp.valueDeclaration)) {
const {initializer} = sourceProp.valueDeclaration;
if (initializer && isJsxExpression(initializer)) {
const {expression} = initializer;
if (expression && isIdentifier(expression)) {
assignmentVariance = VarianceFlags.Invariant;
}
}
}
}

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

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

Expand Down
102 changes: 102 additions & 0 deletions tests/baselines/reference/strictAssignment5.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//// [strictAssignment5.tsx]
/// <reference path="/.lib/react16.d.ts" />
import React from "react";

module StrictAssignment5 {
class Animal {}
class Cat { purr() {} }
class Dog { bark() {} }

type Props = {
animals: Animal[],
};

class Foo extends React.Component<Props> {
render() {
return "foo";
}
}

const cats: Cat[] = [new Cat];
<Foo animals={cats} />; // error
<Foo animals={[new Cat]} />; // okay

type ReadonlyProps = {
animals: ReadonlyArray<Animal>,
};

class Bar extends React.Component<ReadonlyProps> {
render() {
return "foo";
}
}

<Bar animals={cats} />; // okay
}


//// [strictAssignment5.js]
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
exports.__esModule = true;
/// <reference path="react16.d.ts" />
var react_1 = __importDefault(require("react"));
var StrictAssignment5;
(function (StrictAssignment5) {
var Animal = /** @class */ (function () {
function Animal() {
}
return Animal;
}());
var Cat = /** @class */ (function () {
function Cat() {
}
Cat.prototype.purr = function () { };
return Cat;
}());
var Dog = /** @class */ (function () {
function Dog() {
}
Dog.prototype.bark = function () { };
return Dog;
}());
var Foo = /** @class */ (function (_super) {
__extends(Foo, _super);
function Foo() {
return _super !== null && _super.apply(this, arguments) || this;
}
Foo.prototype.render = function () {
return "foo";
};
return Foo;
}(react_1["default"].Component));
var cats = [new Cat];
react_1["default"].createElement(Foo, { animals: cats }); // error
react_1["default"].createElement(Foo, { animals: [new Cat] }); // okay
var Bar = /** @class */ (function (_super) {
__extends(Bar, _super);
function Bar() {
return _super !== null && _super.apply(this, arguments) || this;
}
Bar.prototype.render = function () {
return "foo";
};
return Bar;
}(react_1["default"].Component));
react_1["default"].createElement(Bar, { animals: cats }); // okay
})(StrictAssignment5 || (StrictAssignment5 = {}));
87 changes: 87 additions & 0 deletions tests/baselines/reference/strictAssignment5.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
=== tests/cases/compiler/strictAssignment5.tsx ===
/// <reference path="react16.d.ts" />
import React from "react";
>React : Symbol(React, Decl(strictAssignment5.tsx, 1, 6))

module StrictAssignment5 {
>StrictAssignment5 : Symbol(StrictAssignment5, Decl(strictAssignment5.tsx, 1, 26))

class Animal {}
>Animal : Symbol(Animal, Decl(strictAssignment5.tsx, 3, 26))

class Cat { purr() {} }
>Cat : Symbol(Cat, Decl(strictAssignment5.tsx, 4, 19))
>purr : Symbol(Cat.purr, Decl(strictAssignment5.tsx, 5, 15))

class Dog { bark() {} }
>Dog : Symbol(Dog, Decl(strictAssignment5.tsx, 5, 27))
>bark : Symbol(Dog.bark, Decl(strictAssignment5.tsx, 6, 15))

type Props = {
>Props : Symbol(Props, Decl(strictAssignment5.tsx, 6, 27))

animals: Animal[],
>animals : Symbol(animals, Decl(strictAssignment5.tsx, 8, 18))
>Animal : Symbol(Animal, Decl(strictAssignment5.tsx, 3, 26))

};

class Foo extends React.Component<Props> {
>Foo : Symbol(Foo, Decl(strictAssignment5.tsx, 10, 6))
>React.Component : Symbol(React.Component, Decl(react16.d.ts, 345, 54), Decl(react16.d.ts, 349, 94))
>React : Symbol(React, Decl(strictAssignment5.tsx, 1, 6))
>Component : Symbol(React.Component, Decl(react16.d.ts, 345, 54), Decl(react16.d.ts, 349, 94))
>Props : Symbol(Props, Decl(strictAssignment5.tsx, 6, 27))

render() {
>render : Symbol(Foo.render, Decl(strictAssignment5.tsx, 12, 46))

return "foo";
}
}

const cats: Cat[] = [new Cat];
>cats : Symbol(cats, Decl(strictAssignment5.tsx, 18, 9))
>Cat : Symbol(Cat, Decl(strictAssignment5.tsx, 4, 19))
>Cat : Symbol(Cat, Decl(strictAssignment5.tsx, 4, 19))

<Foo animals={cats} />; // error
>Foo : Symbol(Foo, Decl(strictAssignment5.tsx, 10, 6))
>animals : Symbol(animals, Decl(strictAssignment5.tsx, 19, 8))
>cats : Symbol(cats, Decl(strictAssignment5.tsx, 18, 9))

<Foo animals={[new Cat]} />; // okay
>Foo : Symbol(Foo, Decl(strictAssignment5.tsx, 10, 6))
>animals : Symbol(animals, Decl(strictAssignment5.tsx, 20, 8))
>Cat : Symbol(Cat, Decl(strictAssignment5.tsx, 4, 19))

type ReadonlyProps = {
>ReadonlyProps : Symbol(ReadonlyProps, Decl(strictAssignment5.tsx, 20, 32))

animals: ReadonlyArray<Animal>,
>animals : Symbol(animals, Decl(strictAssignment5.tsx, 22, 26))
>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --))
>Animal : Symbol(Animal, Decl(strictAssignment5.tsx, 3, 26))

};

class Bar extends React.Component<ReadonlyProps> {
>Bar : Symbol(Bar, Decl(strictAssignment5.tsx, 24, 6))
>React.Component : Symbol(React.Component, Decl(react16.d.ts, 345, 54), Decl(react16.d.ts, 349, 94))
>React : Symbol(React, Decl(strictAssignment5.tsx, 1, 6))
>Component : Symbol(React.Component, Decl(react16.d.ts, 345, 54), Decl(react16.d.ts, 349, 94))
>ReadonlyProps : Symbol(ReadonlyProps, Decl(strictAssignment5.tsx, 20, 32))

render() {
>render : Symbol(Bar.render, Decl(strictAssignment5.tsx, 26, 54))

return "foo";
}
}

<Bar animals={cats} />; // okay
>Bar : Symbol(Bar, Decl(strictAssignment5.tsx, 24, 6))
>animals : Symbol(animals, Decl(strictAssignment5.tsx, 32, 8))
>cats : Symbol(cats, Decl(strictAssignment5.tsx, 18, 9))
}

90 changes: 90 additions & 0 deletions tests/baselines/reference/strictAssignment5.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
=== tests/cases/compiler/strictAssignment5.tsx ===
/// <reference path="react16.d.ts" />
import React from "react";
>React : typeof React

module StrictAssignment5 {
>StrictAssignment5 : typeof StrictAssignment5

class Animal {}
>Animal : Animal

class Cat { purr() {} }
>Cat : Cat
>purr : () => void

class Dog { bark() {} }
>Dog : Dog
>bark : () => void

type Props = {
>Props : { animals: Animal[]; }

animals: Animal[],
>animals : Animal[]

};

class Foo extends React.Component<Props> {
>Foo : Foo
>React.Component : React.Component<{ animals: Animal[]; }, {}, any>
>React : typeof React
>Component : typeof React.Component

render() {
>render : () => string

return "foo";
>"foo" : "foo"
}
}

const cats: Cat[] = [new Cat];
>cats : Cat[]
>[new Cat] : Cat[]
>new Cat : Cat
>Cat : typeof Cat

<Foo animals={cats} />; // error
><Foo animals={cats} /> : JSX.Element
>Foo : typeof Foo
>animals : Cat[]
>cats : Cat[]

<Foo animals={[new Cat]} />; // okay
><Foo animals={[new Cat]} /> : JSX.Element
>Foo : typeof Foo
>animals : Cat[]
>[new Cat] : Cat[]
>new Cat : Cat
>Cat : typeof Cat

type ReadonlyProps = {
>ReadonlyProps : { animals: readonly Animal[]; }

animals: ReadonlyArray<Animal>,
>animals : readonly Animal[]

};

class Bar extends React.Component<ReadonlyProps> {
>Bar : Bar
>React.Component : React.Component<{ animals: readonly Animal[]; }, {}, any>
>React : typeof React
>Component : typeof React.Component

render() {
>render : () => string

return "foo";
>"foo" : "foo"
}
}

<Bar animals={cats} />; // okay
><Bar animals={cats} /> : JSX.Element
>Bar : typeof Bar
>animals : Cat[]
>cats : Cat[]
}

37 changes: 37 additions & 0 deletions tests/cases/compiler/strictAssignment5.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// @jsx: react
// @esModuleInterop: true
// @strict: true
/// <reference path="/.lib/react16.d.ts" />
import React from "react";

module StrictAssignment5 {
class Animal {}
class Cat { purr() {} }
class Dog { bark() {} }

type Props = {
animals: Animal[],
};

class Foo extends React.Component<Props> {
render() {
return "foo";
}
}

const cats: Cat[] = [new Cat];
<Foo animals={cats} />; // error
<Foo animals={[new Cat]} />; // okay

type ReadonlyProps = {
animals: ReadonlyArray<Animal>,
};

class Bar extends React.Component<ReadonlyProps> {
render() {
return "foo";
}
}

<Bar animals={cats} />; // okay
}

0 comments on commit 2f4bd00

Please sign in to comment.