Merge pull request #14216 from Microsoft/master-fix13526

[Master] Fix13526 allow JSX attributes to be union type
This commit is contained in:
Yui 2017-02-22 11:01:08 -08:00 committed by GitHub
commit 31c4ad193f
9 changed files with 341 additions and 42 deletions

View file

@ -12754,11 +12754,6 @@ namespace ts {
// Props is of type 'any' or unknown
return attributesType;
}
else if (attributesType.flags & TypeFlags.Union) {
// Props cannot be a union type
error(openingLikeElement.tagName, Diagnostics.JSX_element_attributes_type_0_may_not_be_a_union_type, typeToString(attributesType));
return anyType;
}
else {
// Normal case -- add in IntrinsicClassElements<T> and IntrinsicElements
let apparentAttributesType = attributesType;

View file

@ -0,0 +1,53 @@
//// [file.tsx]
import React = require('react');
interface Address {
street: string;
country: string;
}
interface CanadianAddress extends Address {
postalCode: string;
}
interface AmericanAddress extends Address {
zipCode: string;
}
type Properties = CanadianAddress | AmericanAddress;
export class AddressComp extends React.Component<Properties, void> {
public render() {
return null;
}
}
let a = <AddressComp postalCode='T1B 0L3' street="vancouver" country="CA" />
//// [file.jsx]
"use strict";
var __extends = (this && this.__extends) || (function () {
var 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 function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
exports.__esModule = true;
var React = require("react");
var AddressComp = (function (_super) {
__extends(AddressComp, _super);
function AddressComp() {
return _super !== null && _super.apply(this, arguments) || this;
}
AddressComp.prototype.render = function () {
return null;
};
return AddressComp;
}(React.Component));
exports.AddressComp = AddressComp;
var a = <AddressComp postalCode='T1B 0L3' street="vancouver" country="CA"/>;

View file

@ -0,0 +1,57 @@
=== tests/cases/conformance/jsx/file.tsx ===
import React = require('react');
>React : Symbol(React, Decl(file.tsx, 0, 0))
interface Address {
>Address : Symbol(Address, Decl(file.tsx, 1, 32))
street: string;
>street : Symbol(Address.street, Decl(file.tsx, 3, 19))
country: string;
>country : Symbol(Address.country, Decl(file.tsx, 4, 17))
}
interface CanadianAddress extends Address {
>CanadianAddress : Symbol(CanadianAddress, Decl(file.tsx, 6, 1))
>Address : Symbol(Address, Decl(file.tsx, 1, 32))
postalCode: string;
>postalCode : Symbol(CanadianAddress.postalCode, Decl(file.tsx, 8, 43))
}
interface AmericanAddress extends Address {
>AmericanAddress : Symbol(AmericanAddress, Decl(file.tsx, 10, 1))
>Address : Symbol(Address, Decl(file.tsx, 1, 32))
zipCode: string;
>zipCode : Symbol(AmericanAddress.zipCode, Decl(file.tsx, 12, 43))
}
type Properties = CanadianAddress | AmericanAddress;
>Properties : Symbol(Properties, Decl(file.tsx, 14, 1))
>CanadianAddress : Symbol(CanadianAddress, Decl(file.tsx, 6, 1))
>AmericanAddress : Symbol(AmericanAddress, Decl(file.tsx, 10, 1))
export class AddressComp extends React.Component<Properties, void> {
>AddressComp : Symbol(AddressComp, Decl(file.tsx, 16, 52))
>React.Component : Symbol(React.Component, Decl(react.d.ts, 158, 55))
>React : Symbol(React, Decl(file.tsx, 0, 0))
>Component : Symbol(React.Component, Decl(react.d.ts, 158, 55))
>Properties : Symbol(Properties, Decl(file.tsx, 14, 1))
public render() {
>render : Symbol(AddressComp.render, Decl(file.tsx, 18, 68))
return null;
}
}
let a = <AddressComp postalCode='T1B 0L3' street="vancouver" country="CA" />
>a : Symbol(a, Decl(file.tsx, 24, 3))
>AddressComp : Symbol(AddressComp, Decl(file.tsx, 16, 52))
>postalCode : Symbol(postalCode, Decl(file.tsx, 24, 20))
>street : Symbol(street, Decl(file.tsx, 24, 41))
>country : Symbol(country, Decl(file.tsx, 24, 60))

View file

@ -0,0 +1,59 @@
=== tests/cases/conformance/jsx/file.tsx ===
import React = require('react');
>React : typeof React
interface Address {
>Address : Address
street: string;
>street : string
country: string;
>country : string
}
interface CanadianAddress extends Address {
>CanadianAddress : CanadianAddress
>Address : Address
postalCode: string;
>postalCode : string
}
interface AmericanAddress extends Address {
>AmericanAddress : AmericanAddress
>Address : Address
zipCode: string;
>zipCode : string
}
type Properties = CanadianAddress | AmericanAddress;
>Properties : CanadianAddress | AmericanAddress
>CanadianAddress : CanadianAddress
>AmericanAddress : AmericanAddress
export class AddressComp extends React.Component<Properties, void> {
>AddressComp : AddressComp
>React.Component : React.Component<CanadianAddress | AmericanAddress, void>
>React : typeof React
>Component : typeof React.Component
>Properties : CanadianAddress | AmericanAddress
public render() {
>render : () => any
return null;
>null : null
}
}
let a = <AddressComp postalCode='T1B 0L3' street="vancouver" country="CA" />
>a : JSX.Element
><AddressComp postalCode='T1B 0L3' street="vancouver" country="CA" /> : JSX.Element
>AddressComp : typeof AddressComp
>postalCode : string
>street : string
>country : string

View file

@ -1,4 +1,7 @@
tests/cases/conformance/jsx/file.tsx(14,10): error TS2600: JSX element attributes type '({ editable: false; } & { children?: ReactNode; }) | ({ editable: true; onEdit: (newText: string) => void; } & { children?: ReactNode; })' may not be a union type.
tests/cases/conformance/jsx/file.tsx(14,24): error TS2322: Type '{ editable: true; }' is not assignable to type '(IntrinsicAttributes & IntrinsicClassAttributes<TextComponent> & { editable: false; } & { children?: ReactNode; }) | (IntrinsicAttributes & IntrinsicClassAttributes<TextComponent> & { editable: true; onEdit: (newText: string) => void; } & { children?: ReactNode; })'.
Type '{ editable: true; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<TextComponent> & { editable: true; onEdit: (newText: string) => void; } & { children?: ReactNode; }'.
Type '{ editable: true; }' is not assignable to type '{ editable: true; onEdit: (newText: string) => void; }'.
Property 'onEdit' is missing in type '{ editable: true; }'.
==== tests/cases/conformance/jsx/file.tsx (1 errors) ====
@ -16,8 +19,11 @@ tests/cases/conformance/jsx/file.tsx(14,10): error TS2600: JSX element attribute
// Error
let x = <TextComponent editable={true} />
~~~~~~~~~~~~~
!!! error TS2600: JSX element attributes type '({ editable: false; } & { children?: ReactNode; }) | ({ editable: true; onEdit: (newText: string) => void; } & { children?: ReactNode; })' may not be a union type.
~~~~~~~~~~~~~~~
!!! error TS2322: Type '{ editable: true; }' is not assignable to type '(IntrinsicAttributes & IntrinsicClassAttributes<TextComponent> & { editable: false; } & { children?: ReactNode; }) | (IntrinsicAttributes & IntrinsicClassAttributes<TextComponent> & { editable: true; onEdit: (newText: string) => void; } & { children?: ReactNode; })'.
!!! error TS2322: Type '{ editable: true; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<TextComponent> & { editable: true; onEdit: (newText: string) => void; } & { children?: ReactNode; }'.
!!! error TS2322: Type '{ editable: true; }' is not assignable to type '{ editable: true; onEdit: (newText: string) => void; }'.
!!! error TS2322: Property 'onEdit' is missing in type '{ editable: true; }'.
const textProps: TextProps = {
editable: false

View file

@ -1,34 +0,0 @@
tests/cases/conformance/jsx/file.tsx(18,11): error TS2600: JSX element attributes type '({ editable: false; } & { children?: ReactNode; }) | ({ editable: true; onEdit: (newText: string) => void; } & { children?: ReactNode; })' may not be a union type.
tests/cases/conformance/jsx/file.tsx(25,11): error TS2600: JSX element attributes type '({ editable: false; } & { children?: ReactNode; }) | ({ editable: true; onEdit: (newText: string) => void; } & { children?: ReactNode; })' may not be a union type.
==== tests/cases/conformance/jsx/file.tsx (2 errors) ====
import React = require('react');
type TextProps = { editable: false }
| { editable: true, onEdit: (newText: string) => void };
class TextComponent extends React.Component<TextProps, {}> {
render() {
return <span>Some Text..</span>;
}
}
// OK
const textPropsFalse: TextProps = {
editable: false
};
let y1 = <TextComponent {...textPropsFalse} />
~~~~~~~~~~~~~
!!! error TS2600: JSX element attributes type '({ editable: false; } & { children?: ReactNode; }) | ({ editable: true; onEdit: (newText: string) => void; } & { children?: ReactNode; })' may not be a union type.
const textPropsTrue: TextProps = {
editable: true,
onEdit: () => {}
};
let y2 = <TextComponent {...textPropsTrue} />
~~~~~~~~~~~~~
!!! error TS2600: JSX element attributes type '({ editable: false; } & { children?: ReactNode; }) | ({ editable: true; onEdit: (newText: string) => void; } & { children?: ReactNode; })' may not be a union type.

View file

@ -0,0 +1,62 @@
=== tests/cases/conformance/jsx/file.tsx ===
import React = require('react');
>React : Symbol(React, Decl(file.tsx, 0, 0))
type TextProps = { editable: false }
>TextProps : Symbol(TextProps, Decl(file.tsx, 1, 32))
>editable : Symbol(editable, Decl(file.tsx, 3, 18))
| { editable: true, onEdit: (newText: string) => void };
>editable : Symbol(editable, Decl(file.tsx, 4, 18))
>onEdit : Symbol(onEdit, Decl(file.tsx, 4, 34))
>newText : Symbol(newText, Decl(file.tsx, 4, 44))
class TextComponent extends React.Component<TextProps, {}> {
>TextComponent : Symbol(TextComponent, Decl(file.tsx, 4, 71))
>React.Component : Symbol(React.Component, Decl(react.d.ts, 158, 55))
>React : Symbol(React, Decl(file.tsx, 0, 0))
>Component : Symbol(React.Component, Decl(react.d.ts, 158, 55))
>TextProps : Symbol(TextProps, Decl(file.tsx, 1, 32))
render() {
>render : Symbol(TextComponent.render, Decl(file.tsx, 6, 60))
return <span>Some Text..</span>;
>span : Symbol(JSX.IntrinsicElements.span, Decl(react.d.ts, 2458, 51))
>span : Symbol(JSX.IntrinsicElements.span, Decl(react.d.ts, 2458, 51))
}
}
// OK
const textPropsFalse: TextProps = {
>textPropsFalse : Symbol(textPropsFalse, Decl(file.tsx, 13, 5))
>TextProps : Symbol(TextProps, Decl(file.tsx, 1, 32))
editable: false
>editable : Symbol(editable, Decl(file.tsx, 13, 35))
};
let y1 = <TextComponent {...textPropsFalse} />
>y1 : Symbol(y1, Decl(file.tsx, 17, 3))
>TextComponent : Symbol(TextComponent, Decl(file.tsx, 4, 71))
>textPropsFalse : Symbol(textPropsFalse, Decl(file.tsx, 13, 5))
const textPropsTrue: TextProps = {
>textPropsTrue : Symbol(textPropsTrue, Decl(file.tsx, 19, 5))
>TextProps : Symbol(TextProps, Decl(file.tsx, 1, 32))
editable: true,
>editable : Symbol(editable, Decl(file.tsx, 19, 34))
onEdit: () => {}
>onEdit : Symbol(onEdit, Decl(file.tsx, 20, 19))
};
let y2 = <TextComponent {...textPropsTrue} />
>y2 : Symbol(y2, Decl(file.tsx, 24, 3))
>TextComponent : Symbol(TextComponent, Decl(file.tsx, 4, 71))
>textPropsTrue : Symbol(textPropsTrue, Decl(file.tsx, 19, 5))

View file

@ -0,0 +1,72 @@
=== tests/cases/conformance/jsx/file.tsx ===
import React = require('react');
>React : typeof React
type TextProps = { editable: false }
>TextProps : { editable: false; } | { editable: true; onEdit: (newText: string) => void; }
>editable : false
>false : false
| { editable: true, onEdit: (newText: string) => void };
>editable : true
>true : true
>onEdit : (newText: string) => void
>newText : string
class TextComponent extends React.Component<TextProps, {}> {
>TextComponent : TextComponent
>React.Component : React.Component<{ editable: false; } | { editable: true; onEdit: (newText: string) => void; }, {}>
>React : typeof React
>Component : typeof React.Component
>TextProps : { editable: false; } | { editable: true; onEdit: (newText: string) => void; }
render() {
>render : () => JSX.Element
return <span>Some Text..</span>;
><span>Some Text..</span> : JSX.Element
>span : any
>span : any
}
}
// OK
const textPropsFalse: TextProps = {
>textPropsFalse : { editable: false; } | { editable: true; onEdit: (newText: string) => void; }
>TextProps : { editable: false; } | { editable: true; onEdit: (newText: string) => void; }
>{ editable: false} : { editable: false; }
editable: false
>editable : boolean
>false : false
};
let y1 = <TextComponent {...textPropsFalse} />
>y1 : JSX.Element
><TextComponent {...textPropsFalse} /> : JSX.Element
>TextComponent : typeof TextComponent
>textPropsFalse : { editable: false; }
const textPropsTrue: TextProps = {
>textPropsTrue : { editable: false; } | { editable: true; onEdit: (newText: string) => void; }
>TextProps : { editable: false; } | { editable: true; onEdit: (newText: string) => void; }
>{ editable: true, onEdit: () => {}} : { editable: true; onEdit: () => void; }
editable: true,
>editable : boolean
>true : true
onEdit: () => {}
>onEdit : () => void
>() => {} : () => void
};
let y2 = <TextComponent {...textPropsTrue} />
>y2 : JSX.Element
><TextComponent {...textPropsTrue} /> : JSX.Element
>TextComponent : typeof TextComponent
>textPropsTrue : { editable: true; onEdit: (newText: string) => void; }

View file

@ -0,0 +1,29 @@
// @filename: file.tsx
// @jsx: preserve
// @noLib: true
// @libFiles: react.d.ts,lib.d.ts
import React = require('react');
interface Address {
street: string;
country: string;
}
interface CanadianAddress extends Address {
postalCode: string;
}
interface AmericanAddress extends Address {
zipCode: string;
}
type Properties = CanadianAddress | AmericanAddress;
export class AddressComp extends React.Component<Properties, void> {
public render() {
return null;
}
}
let a = <AddressComp postalCode='T1B 0L3' street="vancouver" country="CA" />