mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-10 18:04:18 -05:00
Extra check in assignment of intersections with generic constituents (#37537)
* Consolidated extra property check with intersections * Fix comment * Add tests * Properly propagate intersectionState * Route property check through recursive type tracking logic * Accept new baselines * Skip check when apparent type of source is never * Accept new baselines * Only check when apparent type of source is a structured type
This commit is contained in:
@@ -195,7 +195,7 @@ namespace ts {
|
||||
None = 0,
|
||||
Source = 1 << 0,
|
||||
Target = 1 << 1,
|
||||
ExcessCheck = 1 << 2,
|
||||
PropertyCheck = 1 << 2,
|
||||
}
|
||||
|
||||
const enum MappedTypeModifiers {
|
||||
@@ -15561,7 +15561,7 @@ namespace ts {
|
||||
if (source.flags & TypeFlags.Union) {
|
||||
result = relation === comparableRelation ?
|
||||
someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), intersectionState) :
|
||||
eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), intersectionState & IntersectionState.ExcessCheck);
|
||||
eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), intersectionState);
|
||||
}
|
||||
else {
|
||||
if (target.flags & TypeFlags.Union) {
|
||||
@@ -15569,12 +15569,6 @@ namespace ts {
|
||||
}
|
||||
else if (target.flags & TypeFlags.Intersection) {
|
||||
result = typeRelatedToEachType(getRegularTypeOfObjectLiteral(source), target as IntersectionType, reportErrors, IntersectionState.Target);
|
||||
if (result && (isPerformingExcessPropertyChecks || isPerformingCommonPropertyChecks) && !(intersectionState & IntersectionState.ExcessCheck)) {
|
||||
// Validate against excess props using the original `source`
|
||||
if (!propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, IntersectionState.ExcessCheck)) {
|
||||
return Ternary.False;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (source.flags & TypeFlags.Intersection) {
|
||||
// Check to see if any constituents of the intersection are immediately related to the target.
|
||||
@@ -15590,9 +15584,7 @@ namespace ts {
|
||||
//
|
||||
// - For a primitive type or type parameter (such as 'number = A & B') there is no point in
|
||||
// breaking the intersection apart.
|
||||
if (!isNonGenericObjectType(target) || !every((<IntersectionType>source).types, t => isNonGenericObjectType(t) && !(getObjectFlags(t) & ObjectFlags.NonInferrableType))) {
|
||||
result = someTypeRelatedToType(<IntersectionType>source, target, /*reportErrors*/ false, IntersectionState.Source);
|
||||
}
|
||||
result = someTypeRelatedToType(<IntersectionType>source, target, /*reportErrors*/ false, IntersectionState.Source);
|
||||
}
|
||||
if (!result && (source.flags & TypeFlags.StructuredOrInstantiable || target.flags & TypeFlags.StructuredOrInstantiable)) {
|
||||
if (result = recursiveTypeRelatedTo(source, target, reportErrors, intersectionState)) {
|
||||
@@ -15624,6 +15616,23 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
}
|
||||
// For certain combinations involving intersections and optional, excess, or mismatched properties we need
|
||||
// an extra property check where the intersection is viewed as a single object. The following are motivating
|
||||
// examples that all should be errors, but aren't without this extra property check:
|
||||
//
|
||||
// let obj: { a: { x: string } } & { c: number } = { a: { x: 'hello', y: 2 }, c: 5 }; // Nested excess property
|
||||
//
|
||||
// declare let wrong: { a: { y: string } };
|
||||
// let weak: { a?: { x?: number } } & { c?: string } = wrong; // Nested weak object type
|
||||
//
|
||||
// function foo<T extends object>(x: { a?: string }, y: T & { a: boolean }) {
|
||||
// x = y; // Mismatched property in source intersection
|
||||
// }
|
||||
if (result && (
|
||||
target.flags & TypeFlags.Intersection && (isPerformingExcessPropertyChecks || isPerformingCommonPropertyChecks) ||
|
||||
isNonGenericObjectType(target) && source.flags & TypeFlags.Intersection && getApparentType(source).flags & TypeFlags.StructuredType && !some((<IntersectionType>source).types, t => !!(getObjectFlags(t) & ObjectFlags.NonInferrableType)))) {
|
||||
result &= recursiveTypeRelatedTo(source, target, reportErrors, IntersectionState.PropertyCheck);
|
||||
}
|
||||
|
||||
if (!result && reportErrors) {
|
||||
source = originalSource.aliasSymbol ? originalSource : source;
|
||||
@@ -16000,6 +16009,9 @@ namespace ts {
|
||||
}
|
||||
|
||||
function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary {
|
||||
if (intersectionState & IntersectionState.PropertyCheck) {
|
||||
return propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, IntersectionState.None);
|
||||
}
|
||||
const flags = source.flags & target.flags;
|
||||
if (relation === identityRelation && !(flags & TypeFlags.Object)) {
|
||||
if (flags & TypeFlags.Index) {
|
||||
|
||||
@@ -5,23 +5,20 @@ tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.t
|
||||
Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"] & ChannelOfType<T, EmailChannel>["type"]'.
|
||||
Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"] & ChannelOfType<T, EmailChannel>["type"]'.
|
||||
Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
Type '"text"' is not assignable to type 'T & "text"'.
|
||||
Type '"text"' is not assignable to type 'T'.
|
||||
'"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
|
||||
Type 'T' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
Type '"text"' is not assignable to type 'T & "text"'.
|
||||
Type '"text"' is not assignable to type 'T'.
|
||||
'"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
|
||||
Type 'T' is not assignable to type 'T & "text"'.
|
||||
Type '"text" | "email"' is not assignable to type 'T & "text"'.
|
||||
Type '"text"' is not assignable to type 'T & "text"'.
|
||||
Type '"text"' is not assignable to type 'T'.
|
||||
'"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
|
||||
Type 'T' is not assignable to type '"text"'.
|
||||
Type '"text" | "email"' is not assignable to type '"text"'.
|
||||
Type '"email"' is not assignable to type '"text"'.
|
||||
Type 'T' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
Type '"text"' is not assignable to type 'T & "text"'.
|
||||
Type '"text"' is not assignable to type 'T'.
|
||||
'"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
|
||||
Type 'T' is not assignable to type 'T & "text"'.
|
||||
Type '"text" | "email"' is not assignable to type 'T & "text"'.
|
||||
Type '"text"' is not assignable to type 'T & "text"'.
|
||||
Type '"text"' is not assignable to type 'T'.
|
||||
'"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
|
||||
Type 'T' is not assignable to type '"text"'.
|
||||
Type '"text" | "email"' is not assignable to type '"text"'.
|
||||
Type '"email"' is not assignable to type '"text"'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts (1 errors) ====
|
||||
@@ -66,23 +63,20 @@ tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.t
|
||||
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"] & ChannelOfType<T, EmailChannel>["type"]'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"] & ChannelOfType<T, EmailChannel>["type"]'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'T'.
|
||||
!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
|
||||
!!! error TS2322: Type 'T' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'T'.
|
||||
!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
|
||||
!!! error TS2322: Type 'T' is not assignable to type 'T & "text"'.
|
||||
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'T & "text"'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'T'.
|
||||
!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
|
||||
!!! error TS2322: Type 'T' is not assignable to type '"text"'.
|
||||
!!! error TS2322: Type '"text" | "email"' is not assignable to type '"text"'.
|
||||
!!! error TS2322: Type '"email"' is not assignable to type '"text"'.
|
||||
!!! error TS2322: Type 'T' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'T'.
|
||||
!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
|
||||
!!! error TS2322: Type 'T' is not assignable to type 'T & "text"'.
|
||||
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'T & "text"'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'.
|
||||
!!! error TS2322: Type '"text"' is not assignable to type 'T'.
|
||||
!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
|
||||
!!! error TS2322: Type 'T' is not assignable to type '"text"'.
|
||||
!!! error TS2322: Type '"text" | "email"' is not assignable to type '"text"'.
|
||||
!!! error TS2322: Type '"email"' is not assignable to type '"text"'.
|
||||
}
|
||||
|
||||
const newTextChannel = makeNewChannel('text');
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
tests/cases/compiler/intersectionPropertyCheck.ts(1,68): error TS2322: Type '{ x: string; y: number; }' is not assignable to type '{ x: string; }'.
|
||||
Object literal may only specify known properties, and 'y' does not exist in type '{ x: string; }'.
|
||||
tests/cases/compiler/intersectionPropertyCheck.ts(4,5): error TS2322: Type '{ a: { y: string; }; }' is not assignable to type '{ a?: { x?: number | undefined; } | undefined; } & { c?: string | undefined; }'.
|
||||
Types of property 'a' are incompatible.
|
||||
Type '{ y: string; }' has no properties in common with type '{ x?: number | undefined; }'.
|
||||
tests/cases/compiler/intersectionPropertyCheck.ts(7,3): error TS2322: Type 'T & { a: boolean; }' is not assignable to type '{ a?: string | undefined; }'.
|
||||
Types of property 'a' are incompatible.
|
||||
Type 'boolean' is not assignable to type 'string | undefined'.
|
||||
tests/cases/compiler/intersectionPropertyCheck.ts(17,22): error TS2322: Type 'true' is not assignable to type 'string[] | undefined'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/intersectionPropertyCheck.ts (4 errors) ====
|
||||
let obj: { a: { x: string } } & { c: number } = { a: { x: 'hello', y: 2 }, c: 5 }; // Nested excess property
|
||||
~~~~
|
||||
!!! error TS2322: Type '{ x: string; y: number; }' is not assignable to type '{ x: string; }'.
|
||||
!!! error TS2322: Object literal may only specify known properties, and 'y' does not exist in type '{ x: string; }'.
|
||||
!!! related TS6500 tests/cases/compiler/intersectionPropertyCheck.ts:1:12: The expected type comes from property 'a' which is declared here on type '{ a: { x: string; }; } & { c: number; }'
|
||||
|
||||
declare let wrong: { a: { y: string } };
|
||||
let weak: { a?: { x?: number } } & { c?: string } = wrong; // Nested weak object type
|
||||
~~~~
|
||||
!!! error TS2322: Type '{ a: { y: string; }; }' is not assignable to type '{ a?: { x?: number | undefined; } | undefined; } & { c?: string | undefined; }'.
|
||||
!!! error TS2322: Types of property 'a' are incompatible.
|
||||
!!! error TS2322: Type '{ y: string; }' has no properties in common with type '{ x?: number | undefined; }'.
|
||||
|
||||
function foo<T extends object>(x: { a?: string }, y: T & { a: boolean }) {
|
||||
x = y; // Mismatched property in source intersection
|
||||
~
|
||||
!!! error TS2322: Type 'T & { a: boolean; }' is not assignable to type '{ a?: string | undefined; }'.
|
||||
!!! error TS2322: Types of property 'a' are incompatible.
|
||||
!!! error TS2322: Type 'boolean' is not assignable to type 'string | undefined'.
|
||||
}
|
||||
|
||||
// Repro from #36637
|
||||
|
||||
interface Test {
|
||||
readonly hi?: string[]
|
||||
}
|
||||
|
||||
function test<T extends object>(value: T): Test {
|
||||
return { ...value, hi: true }
|
||||
~~
|
||||
!!! error TS2322: Type 'true' is not assignable to type 'string[] | undefined'.
|
||||
!!! related TS6500 tests/cases/compiler/intersectionPropertyCheck.ts:13:12: The expected type comes from property 'hi' which is declared here on type 'Test'
|
||||
}
|
||||
|
||||
42
tests/baselines/reference/intersectionPropertyCheck.js
Normal file
42
tests/baselines/reference/intersectionPropertyCheck.js
Normal file
@@ -0,0 +1,42 @@
|
||||
//// [intersectionPropertyCheck.ts]
|
||||
let obj: { a: { x: string } } & { c: number } = { a: { x: 'hello', y: 2 }, c: 5 }; // Nested excess property
|
||||
|
||||
declare let wrong: { a: { y: string } };
|
||||
let weak: { a?: { x?: number } } & { c?: string } = wrong; // Nested weak object type
|
||||
|
||||
function foo<T extends object>(x: { a?: string }, y: T & { a: boolean }) {
|
||||
x = y; // Mismatched property in source intersection
|
||||
}
|
||||
|
||||
// Repro from #36637
|
||||
|
||||
interface Test {
|
||||
readonly hi?: string[]
|
||||
}
|
||||
|
||||
function test<T extends object>(value: T): Test {
|
||||
return { ...value, hi: true }
|
||||
}
|
||||
|
||||
|
||||
//// [intersectionPropertyCheck.js]
|
||||
"use strict";
|
||||
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);
|
||||
};
|
||||
var obj = { a: { x: 'hello', y: 2 }, c: 5 }; // Nested excess property
|
||||
var weak = wrong; // Nested weak object type
|
||||
function foo(x, y) {
|
||||
x = y; // Mismatched property in source intersection
|
||||
}
|
||||
function test(value) {
|
||||
return __assign(__assign({}, value), { hi: true });
|
||||
}
|
||||
58
tests/baselines/reference/intersectionPropertyCheck.symbols
Normal file
58
tests/baselines/reference/intersectionPropertyCheck.symbols
Normal file
@@ -0,0 +1,58 @@
|
||||
=== tests/cases/compiler/intersectionPropertyCheck.ts ===
|
||||
let obj: { a: { x: string } } & { c: number } = { a: { x: 'hello', y: 2 }, c: 5 }; // Nested excess property
|
||||
>obj : Symbol(obj, Decl(intersectionPropertyCheck.ts, 0, 3))
|
||||
>a : Symbol(a, Decl(intersectionPropertyCheck.ts, 0, 10))
|
||||
>x : Symbol(x, Decl(intersectionPropertyCheck.ts, 0, 15))
|
||||
>c : Symbol(c, Decl(intersectionPropertyCheck.ts, 0, 33))
|
||||
>a : Symbol(a, Decl(intersectionPropertyCheck.ts, 0, 49))
|
||||
>x : Symbol(x, Decl(intersectionPropertyCheck.ts, 0, 54))
|
||||
>y : Symbol(y, Decl(intersectionPropertyCheck.ts, 0, 66))
|
||||
>c : Symbol(c, Decl(intersectionPropertyCheck.ts, 0, 74))
|
||||
|
||||
declare let wrong: { a: { y: string } };
|
||||
>wrong : Symbol(wrong, Decl(intersectionPropertyCheck.ts, 2, 11))
|
||||
>a : Symbol(a, Decl(intersectionPropertyCheck.ts, 2, 20))
|
||||
>y : Symbol(y, Decl(intersectionPropertyCheck.ts, 2, 25))
|
||||
|
||||
let weak: { a?: { x?: number } } & { c?: string } = wrong; // Nested weak object type
|
||||
>weak : Symbol(weak, Decl(intersectionPropertyCheck.ts, 3, 3))
|
||||
>a : Symbol(a, Decl(intersectionPropertyCheck.ts, 3, 11))
|
||||
>x : Symbol(x, Decl(intersectionPropertyCheck.ts, 3, 17))
|
||||
>c : Symbol(c, Decl(intersectionPropertyCheck.ts, 3, 36))
|
||||
>wrong : Symbol(wrong, Decl(intersectionPropertyCheck.ts, 2, 11))
|
||||
|
||||
function foo<T extends object>(x: { a?: string }, y: T & { a: boolean }) {
|
||||
>foo : Symbol(foo, Decl(intersectionPropertyCheck.ts, 3, 58))
|
||||
>T : Symbol(T, Decl(intersectionPropertyCheck.ts, 5, 13))
|
||||
>x : Symbol(x, Decl(intersectionPropertyCheck.ts, 5, 31))
|
||||
>a : Symbol(a, Decl(intersectionPropertyCheck.ts, 5, 35))
|
||||
>y : Symbol(y, Decl(intersectionPropertyCheck.ts, 5, 49))
|
||||
>T : Symbol(T, Decl(intersectionPropertyCheck.ts, 5, 13))
|
||||
>a : Symbol(a, Decl(intersectionPropertyCheck.ts, 5, 58))
|
||||
|
||||
x = y; // Mismatched property in source intersection
|
||||
>x : Symbol(x, Decl(intersectionPropertyCheck.ts, 5, 31))
|
||||
>y : Symbol(y, Decl(intersectionPropertyCheck.ts, 5, 49))
|
||||
}
|
||||
|
||||
// Repro from #36637
|
||||
|
||||
interface Test {
|
||||
>Test : Symbol(Test, Decl(intersectionPropertyCheck.ts, 7, 1))
|
||||
|
||||
readonly hi?: string[]
|
||||
>hi : Symbol(Test.hi, Decl(intersectionPropertyCheck.ts, 11, 16))
|
||||
}
|
||||
|
||||
function test<T extends object>(value: T): Test {
|
||||
>test : Symbol(test, Decl(intersectionPropertyCheck.ts, 13, 1))
|
||||
>T : Symbol(T, Decl(intersectionPropertyCheck.ts, 15, 14))
|
||||
>value : Symbol(value, Decl(intersectionPropertyCheck.ts, 15, 32))
|
||||
>T : Symbol(T, Decl(intersectionPropertyCheck.ts, 15, 14))
|
||||
>Test : Symbol(Test, Decl(intersectionPropertyCheck.ts, 7, 1))
|
||||
|
||||
return { ...value, hi: true }
|
||||
>value : Symbol(value, Decl(intersectionPropertyCheck.ts, 15, 32))
|
||||
>hi : Symbol(hi, Decl(intersectionPropertyCheck.ts, 16, 20))
|
||||
}
|
||||
|
||||
59
tests/baselines/reference/intersectionPropertyCheck.types
Normal file
59
tests/baselines/reference/intersectionPropertyCheck.types
Normal file
@@ -0,0 +1,59 @@
|
||||
=== tests/cases/compiler/intersectionPropertyCheck.ts ===
|
||||
let obj: { a: { x: string } } & { c: number } = { a: { x: 'hello', y: 2 }, c: 5 }; // Nested excess property
|
||||
>obj : { a: { x: string;}; } & { c: number; }
|
||||
>a : { x: string; }
|
||||
>x : string
|
||||
>c : number
|
||||
>{ a: { x: 'hello', y: 2 }, c: 5 } : { a: { x: string; y: number; }; c: number; }
|
||||
>a : { x: string; y: number; }
|
||||
>{ x: 'hello', y: 2 } : { x: string; y: number; }
|
||||
>x : string
|
||||
>'hello' : "hello"
|
||||
>y : number
|
||||
>2 : 2
|
||||
>c : number
|
||||
>5 : 5
|
||||
|
||||
declare let wrong: { a: { y: string } };
|
||||
>wrong : { a: { y: string;}; }
|
||||
>a : { y: string; }
|
||||
>y : string
|
||||
|
||||
let weak: { a?: { x?: number } } & { c?: string } = wrong; // Nested weak object type
|
||||
>weak : { a?: { x?: number | undefined; } | undefined; } & { c?: string | undefined; }
|
||||
>a : { x?: number | undefined; } | undefined
|
||||
>x : number | undefined
|
||||
>c : string | undefined
|
||||
>wrong : { a: { y: string; }; }
|
||||
|
||||
function foo<T extends object>(x: { a?: string }, y: T & { a: boolean }) {
|
||||
>foo : <T extends object>(x: { a?: string;}, y: T & { a: boolean;}) => void
|
||||
>x : { a?: string | undefined; }
|
||||
>a : string | undefined
|
||||
>y : T & { a: boolean; }
|
||||
>a : boolean
|
||||
|
||||
x = y; // Mismatched property in source intersection
|
||||
>x = y : T & { a: boolean; }
|
||||
>x : { a?: string | undefined; }
|
||||
>y : T & { a: boolean; }
|
||||
}
|
||||
|
||||
// Repro from #36637
|
||||
|
||||
interface Test {
|
||||
readonly hi?: string[]
|
||||
>hi : string[] | undefined
|
||||
}
|
||||
|
||||
function test<T extends object>(value: T): Test {
|
||||
>test : <T extends object>(value: T) => Test
|
||||
>value : T
|
||||
|
||||
return { ...value, hi: true }
|
||||
>{ ...value, hi: true } : T & { hi: boolean; }
|
||||
>value : T
|
||||
>hi : boolean
|
||||
>true : true
|
||||
}
|
||||
|
||||
@@ -5,8 +5,9 @@ tests/cases/compiler/weakType.ts(18,13): error TS2559: Type '12' has no properti
|
||||
tests/cases/compiler/weakType.ts(19,13): error TS2559: Type '"completely wrong"' has no properties in common with type 'Settings'.
|
||||
tests/cases/compiler/weakType.ts(20,13): error TS2559: Type 'false' has no properties in common with type 'Settings'.
|
||||
tests/cases/compiler/weakType.ts(37,18): error TS2559: Type '{ error?: number; }' has no properties in common with type 'ChangeOptions'.
|
||||
tests/cases/compiler/weakType.ts(62,5): error TS2326: Types of property 'properties' are incompatible.
|
||||
Type '{ wrong: string; }' has no properties in common with type '{ b?: number; }'.
|
||||
tests/cases/compiler/weakType.ts(62,5): error TS2322: Type '{ properties: { wrong: string; }; }' is not assignable to type 'Weak & Spoiler'.
|
||||
Types of property 'properties' are incompatible.
|
||||
Type '{ wrong: string; }' has no properties in common with type '{ b?: number; }'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/weakType.ts (8 errors) ====
|
||||
@@ -90,7 +91,8 @@ tests/cases/compiler/weakType.ts(62,5): error TS2326: Types of property 'propert
|
||||
}
|
||||
let weak: Weak & Spoiler = propertiesWrong
|
||||
~~~~
|
||||
!!! error TS2326: Types of property 'properties' are incompatible.
|
||||
!!! error TS2326: Type '{ wrong: string; }' has no properties in common with type '{ b?: number; }'.
|
||||
!!! error TS2322: Type '{ properties: { wrong: string; }; }' is not assignable to type 'Weak & Spoiler'.
|
||||
!!! error TS2322: Types of property 'properties' are incompatible.
|
||||
!!! error TS2322: Type '{ wrong: string; }' has no properties in common with type '{ b?: number; }'.
|
||||
|
||||
|
||||
20
tests/cases/compiler/intersectionPropertyCheck.ts
Normal file
20
tests/cases/compiler/intersectionPropertyCheck.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
// @strict: true
|
||||
|
||||
let obj: { a: { x: string } } & { c: number } = { a: { x: 'hello', y: 2 }, c: 5 }; // Nested excess property
|
||||
|
||||
declare let wrong: { a: { y: string } };
|
||||
let weak: { a?: { x?: number } } & { c?: string } = wrong; // Nested weak object type
|
||||
|
||||
function foo<T extends object>(x: { a?: string }, y: T & { a: boolean }) {
|
||||
x = y; // Mismatched property in source intersection
|
||||
}
|
||||
|
||||
// Repro from #36637
|
||||
|
||||
interface Test {
|
||||
readonly hi?: string[]
|
||||
}
|
||||
|
||||
function test<T extends object>(value: T): Test {
|
||||
return { ...value, hi: true }
|
||||
}
|
||||
Reference in New Issue
Block a user