Merge pull request #14216 from Microsoft/master-fix13526
[Master] Fix13526 allow JSX attributes to be union type
This commit is contained in:
commit
31c4ad193f
9 changed files with 341 additions and 42 deletions
|
@ -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;
|
||||
|
|
53
tests/baselines/reference/tsxAttributeResolution16.js
Normal file
53
tests/baselines/reference/tsxAttributeResolution16.js
Normal 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"/>;
|
57
tests/baselines/reference/tsxAttributeResolution16.symbols
Normal file
57
tests/baselines/reference/tsxAttributeResolution16.symbols
Normal 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))
|
||||
|
59
tests/baselines/reference/tsxAttributeResolution16.types
Normal file
59
tests/baselines/reference/tsxAttributeResolution16.types
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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.
|
|
@ -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))
|
||||
|
|
@ -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; }
|
||||
|
29
tests/cases/conformance/jsx/tsxAttributeResolution16.tsx
Normal file
29
tests/cases/conformance/jsx/tsxAttributeResolution16.tsx
Normal 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" />
|
Loading…
Reference in a new issue