Allow intersections of readonlys to be assignable to a readonly intersection (#28218)

* Allow intersections of readonlys to be assignable to a readonly intersection

* Add real motivating react example to test suite
This commit is contained in:
Wesley Wigham
2018-10-31 14:09:12 -07:00
committed by GitHub
parent 0ef844ff2b
commit 3a2f7c0df1
9 changed files with 221 additions and 1 deletions
+3 -1
View File
@@ -12036,13 +12036,15 @@ namespace ts {
return Ternary.True;
}
// A source type T is related to a target type { [P in keyof T]: X } if T[P] is related to X.
if (!isGenericMappedType(source) && getConstraintTypeFromMappedType(target) === getIndexType(source)) {
if (!isGenericMappedType(source) && isRelatedTo(getConstraintTypeFromMappedType(target), getIndexType(source))) {
const indexedAccessType = getIndexedAccessType(source, getTypeParameterFromMappedType(target));
const templateType = getTemplateTypeFromMappedType(target);
if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) {
return result;
}
}
originalErrorInfo = errorInfo;
errorInfo = saveErrorInfo;
}
}
@@ -0,0 +1,16 @@
//// [homomorphicMappedTypeIntersectionAssignability.ts]
function f<TType>(
a: { weak?: string } & Readonly<TType> & { name: "ok" },
b: Readonly<TType & { name: string }>,
c: Readonly<TType> & { name: string }) {
c = a; // Works
b = a; // Should also work
}
//// [homomorphicMappedTypeIntersectionAssignability.js]
"use strict";
function f(a, b, c) {
c = a; // Works
b = a; // Should also work
}
@@ -0,0 +1,33 @@
=== tests/cases/compiler/homomorphicMappedTypeIntersectionAssignability.ts ===
function f<TType>(
>f : Symbol(f, Decl(homomorphicMappedTypeIntersectionAssignability.ts, 0, 0))
>TType : Symbol(TType, Decl(homomorphicMappedTypeIntersectionAssignability.ts, 0, 11))
a: { weak?: string } & Readonly<TType> & { name: "ok" },
>a : Symbol(a, Decl(homomorphicMappedTypeIntersectionAssignability.ts, 0, 18))
>weak : Symbol(weak, Decl(homomorphicMappedTypeIntersectionAssignability.ts, 1, 8))
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
>TType : Symbol(TType, Decl(homomorphicMappedTypeIntersectionAssignability.ts, 0, 11))
>name : Symbol(name, Decl(homomorphicMappedTypeIntersectionAssignability.ts, 1, 46))
b: Readonly<TType & { name: string }>,
>b : Symbol(b, Decl(homomorphicMappedTypeIntersectionAssignability.ts, 1, 60))
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
>TType : Symbol(TType, Decl(homomorphicMappedTypeIntersectionAssignability.ts, 0, 11))
>name : Symbol(name, Decl(homomorphicMappedTypeIntersectionAssignability.ts, 2, 25))
c: Readonly<TType> & { name: string }) {
>c : Symbol(c, Decl(homomorphicMappedTypeIntersectionAssignability.ts, 2, 42))
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
>TType : Symbol(TType, Decl(homomorphicMappedTypeIntersectionAssignability.ts, 0, 11))
>name : Symbol(name, Decl(homomorphicMappedTypeIntersectionAssignability.ts, 3, 26))
c = a; // Works
>c : Symbol(c, Decl(homomorphicMappedTypeIntersectionAssignability.ts, 2, 42))
>a : Symbol(a, Decl(homomorphicMappedTypeIntersectionAssignability.ts, 0, 18))
b = a; // Should also work
>b : Symbol(b, Decl(homomorphicMappedTypeIntersectionAssignability.ts, 1, 60))
>a : Symbol(a, Decl(homomorphicMappedTypeIntersectionAssignability.ts, 0, 18))
}
@@ -0,0 +1,28 @@
=== tests/cases/compiler/homomorphicMappedTypeIntersectionAssignability.ts ===
function f<TType>(
>f : <TType>(a: { weak?: string | undefined; } & Readonly<TType> & { name: "ok"; }, b: Readonly<TType & { name: string; }>, c: Readonly<TType> & { name: string; }) => void
a: { weak?: string } & Readonly<TType> & { name: "ok" },
>a : { weak?: string | undefined; } & Readonly<TType> & { name: "ok"; }
>weak : string | undefined
>name : "ok"
b: Readonly<TType & { name: string }>,
>b : Readonly<TType & { name: string; }>
>name : string
c: Readonly<TType> & { name: string }) {
>c : Readonly<TType> & { name: string; }
>name : string
c = a; // Works
>c = a : { weak?: string | undefined; } & Readonly<TType> & { name: "ok"; }
>c : Readonly<TType> & { name: string; }
>a : { weak?: string | undefined; } & Readonly<TType> & { name: "ok"; }
b = a; // Should also work
>b = a : { weak?: string | undefined; } & Readonly<TType> & { name: "ok"; }
>b : Readonly<TType & { name: string; }>
>a : { weak?: string | undefined; } & Readonly<TType> & { name: "ok"; }
}
@@ -0,0 +1,53 @@
//// [reactReadonlyHOCAssignabilityReal.tsx]
/// <reference path="/.lib/react16.d.ts" />
import * as React from "react";
function myHigherOrderComponent<P>(Inner: React.ComponentClass<P & {name: string}>): React.ComponentClass<P> {
return class OuterComponent extends React.Component<P> {
render() {
return <Inner {...this.props} name="Matt"/>;
}
};
}
//// [reactReadonlyHOCAssignabilityReal.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 __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
exports.__esModule = true;
/// <reference path="react16.d.ts" />
var React = require("react");
function myHigherOrderComponent(Inner) {
return /** @class */ (function (_super) {
__extends(OuterComponent, _super);
function OuterComponent() {
return _super !== null && _super.apply(this, arguments) || this;
}
OuterComponent.prototype.render = function () {
return React.createElement(Inner, __assign({}, this.props, { name: "Matt" }));
};
return OuterComponent;
}(React.Component));
}
@@ -0,0 +1,36 @@
=== tests/cases/compiler/reactReadonlyHOCAssignabilityReal.tsx ===
/// <reference path="react16.d.ts" />
import * as React from "react";
>React : Symbol(React, Decl(reactReadonlyHOCAssignabilityReal.tsx, 1, 6))
function myHigherOrderComponent<P>(Inner: React.ComponentClass<P & {name: string}>): React.ComponentClass<P> {
>myHigherOrderComponent : Symbol(myHigherOrderComponent, Decl(reactReadonlyHOCAssignabilityReal.tsx, 1, 31))
>P : Symbol(P, Decl(reactReadonlyHOCAssignabilityReal.tsx, 3, 32))
>Inner : Symbol(Inner, Decl(reactReadonlyHOCAssignabilityReal.tsx, 3, 35))
>React : Symbol(React, Decl(reactReadonlyHOCAssignabilityReal.tsx, 1, 6))
>ComponentClass : Symbol(React.ComponentClass, Decl(react16.d.ts, 421, 9))
>P : Symbol(P, Decl(reactReadonlyHOCAssignabilityReal.tsx, 3, 32))
>name : Symbol(name, Decl(reactReadonlyHOCAssignabilityReal.tsx, 3, 68))
>React : Symbol(React, Decl(reactReadonlyHOCAssignabilityReal.tsx, 1, 6))
>ComponentClass : Symbol(React.ComponentClass, Decl(react16.d.ts, 421, 9))
>P : Symbol(P, Decl(reactReadonlyHOCAssignabilityReal.tsx, 3, 32))
return class OuterComponent extends React.Component<P> {
>OuterComponent : Symbol(OuterComponent, Decl(reactReadonlyHOCAssignabilityReal.tsx, 4, 10))
>React.Component : Symbol(React.Component, Decl(react16.d.ts, 345, 54), Decl(react16.d.ts, 349, 94))
>React : Symbol(React, Decl(reactReadonlyHOCAssignabilityReal.tsx, 1, 6))
>Component : Symbol(React.Component, Decl(react16.d.ts, 345, 54), Decl(react16.d.ts, 349, 94))
>P : Symbol(P, Decl(reactReadonlyHOCAssignabilityReal.tsx, 3, 32))
render() {
>render : Symbol(OuterComponent.render, Decl(reactReadonlyHOCAssignabilityReal.tsx, 4, 60))
return <Inner {...this.props} name="Matt"/>;
>Inner : Symbol(Inner, Decl(reactReadonlyHOCAssignabilityReal.tsx, 3, 35))
>this.props : Symbol(React.Component.props, Decl(react16.d.ts, 367, 32))
>this : Symbol(OuterComponent, Decl(reactReadonlyHOCAssignabilityReal.tsx, 4, 10))
>props : Symbol(React.Component.props, Decl(react16.d.ts, 367, 32))
>name : Symbol(name, Decl(reactReadonlyHOCAssignabilityReal.tsx, 6, 41))
}
};
}
@@ -0,0 +1,32 @@
=== tests/cases/compiler/reactReadonlyHOCAssignabilityReal.tsx ===
/// <reference path="react16.d.ts" />
import * as React from "react";
>React : typeof React
function myHigherOrderComponent<P>(Inner: React.ComponentClass<P & {name: string}>): React.ComponentClass<P> {
>myHigherOrderComponent : <P>(Inner: React.ComponentClass<P & { name: string; }, any>) => React.ComponentClass<P, any>
>Inner : React.ComponentClass<P & { name: string; }, any>
>React : any
>name : string
>React : any
return class OuterComponent extends React.Component<P> {
>class OuterComponent extends React.Component<P> { render() { return <Inner {...this.props} name="Matt"/>; } } : typeof OuterComponent
>OuterComponent : typeof OuterComponent
>React.Component : React.Component<P, {}, any>
>React : typeof React
>Component : typeof React.Component
render() {
>render : () => JSX.Element
return <Inner {...this.props} name="Matt"/>;
><Inner {...this.props} name="Matt"/> : JSX.Element
>Inner : React.ComponentClass<P & { name: string; }, any>
>this.props : Readonly<{ children?: React.ReactNode; }> & Readonly<P>
>this : this
>props : Readonly<{ children?: React.ReactNode; }> & Readonly<P>
>name : "Matt"
}
};
}
@@ -0,0 +1,8 @@
// @strict: true
function f<TType>(
a: { weak?: string } & Readonly<TType> & { name: "ok" },
b: Readonly<TType & { name: string }>,
c: Readonly<TType> & { name: string }) {
c = a; // Works
b = a; // Should also work
}
@@ -0,0 +1,12 @@
// @strict: true
// @jsx: react
/// <reference path="/.lib/react16.d.ts" />
import * as React from "react";
function myHigherOrderComponent<P>(Inner: React.ComponentClass<P & {name: string}>): React.ComponentClass<P> {
return class OuterComponent extends React.Component<P> {
render() {
return <Inner {...this.props} name="Matt"/>;
}
};
}