Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 34 additions & 34 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11185,13 +11185,10 @@ namespace ts {
}
}

if (relation !== comparableRelation &&
!(source.flags & TypeFlags.Union) &&
!(target.flags & TypeFlags.Union) &&
!isIntersectionConstituent &&
source !== globalObjectType &&
if (relation !== comparableRelation && !isIntersectionConstituent &&
source.flags & (TypeFlags.Primitive | TypeFlags.Object | TypeFlags.Intersection) && source !== globalObjectType &&
target.flags & (TypeFlags.Object | TypeFlags.Intersection) && isWeakType(target) &&
(getPropertiesOfType(source).length > 0 || typeHasCallOrConstructSignatures(source)) &&
isWeakType(target) &&
!hasCommonProperties(source, target)) {
if (reportErrors) {
const calls = getSignaturesOfType(source, SignatureKind.Call);
Expand Down Expand Up @@ -11832,6 +11829,9 @@ namespace ts {
errorInfo = saveErrorInfo;
}
}
else if (isTupleType(source) && (isArrayType(target) || isReadonlyArrayType(target)) || isArrayType(source) && isReadonlyArrayType(target)) {
return isRelatedTo(getIndexTypeOfType(source, IndexKind.Number) || anyType, getIndexTypeOfType(target, IndexKind.Number) || anyType, reportErrors);
}
// Even if relationship doesn't hold for unions, intersections, or generic type references,
// it may hold in a structural comparison.
// In a check of the form X = A & B, we will have previously checked if A relates to X or B relates
Expand Down Expand Up @@ -12011,34 +12011,6 @@ namespace ts {
return result;
}

/**
* A type is 'weak' if it is an object type with at least one optional property
* and no required properties, call/construct signatures or index signatures
*/
function isWeakType(type: Type): boolean {
if (type.flags & TypeFlags.Object) {
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
return resolved.callSignatures.length === 0 && resolved.constructSignatures.length === 0 &&
!resolved.stringIndexInfo && !resolved.numberIndexInfo &&
resolved.properties.length > 0 &&
every(resolved.properties, p => !!(p.flags & SymbolFlags.Optional));
}
if (type.flags & TypeFlags.Intersection) {
return every((<IntersectionType>type).types, isWeakType);
}
return false;
}

function hasCommonProperties(source: Type, target: Type) {
const isComparingJsxAttributes = !!(getObjectFlags(source) & ObjectFlags.JsxAttributes);
for (const prop of getPropertiesOfType(source)) {
if (isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) {
return true;
}
}
return false;
}

function propertiesIdenticalTo(source: Type, target: Type): Ternary {
if (!(source.flags & TypeFlags.Object && target.flags & TypeFlags.Object)) {
return Ternary.False;
Expand Down Expand Up @@ -12283,6 +12255,34 @@ namespace ts {
}
}

/**
* A type is 'weak' if it is an object type with at least one optional property
* and no required properties, call/construct signatures or index signatures
*/
function isWeakType(type: Type): boolean {
if (type.flags & TypeFlags.Object) {
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
return resolved.callSignatures.length === 0 && resolved.constructSignatures.length === 0 &&
!resolved.stringIndexInfo && !resolved.numberIndexInfo &&
resolved.properties.length > 0 &&
every(resolved.properties, p => !!(p.flags & SymbolFlags.Optional));
}
if (type.flags & TypeFlags.Intersection) {
return every((<IntersectionType>type).types, isWeakType);
}
return false;
}

function hasCommonProperties(source: Type, target: Type) {
const isComparingJsxAttributes = !!(getObjectFlags(source) & ObjectFlags.JsxAttributes);
for (const prop of getPropertiesOfType(source)) {
if (isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) {
return true;
}
}
return false;
}

// Return a type reference where the source type parameter is replaced with the target marker
// type, and flag the result as a marker type reference.
function getMarkerTypeReference(type: GenericType, source: TypeParameter, target: Type) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(13,1): error TS2322: Type 'A[]' is not assignable to type 'ReadonlyArray<B>'.
Types of property 'concat' are incompatible.
Type '{ (...items: ConcatArray<A>[]): A[]; (...items: (A | ConcatArray<A>)[]): A[]; }' is not assignable to type '{ (...items: ConcatArray<B>[]): B[]; (...items: (B | ConcatArray<B>)[]): B[]; }'.
Type 'A[]' is not assignable to type 'B[]'.
Type 'A' is not assignable to type 'B'.
Property 'b' is missing in type 'A'.
Type 'A' is not assignable to type 'B'.
Property 'b' is missing in type 'A'.
tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(18,1): error TS2322: Type 'C<A>' is not assignable to type 'ReadonlyArray<B>'.
Types of property 'concat' are incompatible.
Type '{ (...items: ConcatArray<A>[]): A[]; (...items: (A | ConcatArray<A>)[]): A[]; }' is not assignable to type '{ (...items: ConcatArray<B>[]): B[]; (...items: (B | ConcatArray<B>)[]): B[]; }'.
Type 'A[]' is not assignable to type 'B[]'.
Type 'A' is not assignable to type 'B'.


==== tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts (2 errors) ====
Expand All @@ -26,11 +24,8 @@ tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(18,1): error T
rrb = ara; // error: 'A' is not assignable to 'B'
~~~
!!! error TS2322: Type 'A[]' is not assignable to type 'ReadonlyArray<B>'.
!!! error TS2322: Types of property 'concat' are incompatible.
!!! error TS2322: Type '{ (...items: ConcatArray<A>[]): A[]; (...items: (A | ConcatArray<A>)[]): A[]; }' is not assignable to type '{ (...items: ConcatArray<B>[]): B[]; (...items: (B | ConcatArray<B>)[]): B[]; }'.
!!! error TS2322: Type 'A[]' is not assignable to type 'B[]'.
!!! error TS2322: Type 'A' is not assignable to type 'B'.
!!! error TS2322: Property 'b' is missing in type 'A'.
!!! error TS2322: Type 'A' is not assignable to type 'B'.
!!! error TS2322: Property 'b' is missing in type 'A'.

rra = cra;
rra = crb; // OK, C<B> is assignable to ReadonlyArray<A>
Expand All @@ -41,4 +36,5 @@ tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(18,1): error T
!!! error TS2322: Types of property 'concat' are incompatible.
!!! error TS2322: Type '{ (...items: ConcatArray<A>[]): A[]; (...items: (A | ConcatArray<A>)[]): A[]; }' is not assignable to type '{ (...items: ConcatArray<B>[]): B[]; (...items: (B | ConcatArray<B>)[]): B[]; }'.
!!! error TS2322: Type 'A[]' is not assignable to type 'B[]'.
!!! error TS2322: Type 'A' is not assignable to type 'B'.

Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatBetweenTupleAndArray.ts(17,1): error TS2322: Type '[number, string]' is not assignable to type 'number[]'.
Types of property 'pop' are incompatible.
Type '() => string | number' is not assignable to type '() => number'.
Type 'string | number' is not assignable to type 'number'.
Type 'string' is not assignable to type 'number'.
Type 'string | number' is not assignable to type 'number'.
Type 'string' is not assignable to type 'number'.
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatBetweenTupleAndArray.ts(18,1): error TS2322: Type '{}[]' is not assignable to type '[{}]'.
Property '0' is missing in type '{}[]'.

Expand All @@ -27,10 +25,8 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
numArray = numStrTuple;
~~~~~~~~
!!! error TS2322: Type '[number, string]' is not assignable to type 'number[]'.
!!! error TS2322: Types of property 'pop' are incompatible.
!!! error TS2322: Type '() => string | number' is not assignable to type '() => number'.
!!! error TS2322: Type 'string | number' is not assignable to type 'number'.
!!! error TS2322: Type 'string' is not assignable to type 'number'.
!!! error TS2322: Type 'string | number' is not assignable to type 'number'.
!!! error TS2322: Type 'string' is not assignable to type 'number'.
emptyObjTuple = emptyObjArray;
~~~~~~~~~~~~~
!!! error TS2322: Type '{}[]' is not assignable to type '[{}]'.
Expand Down
20 changes: 6 additions & 14 deletions tests/baselines/reference/for-of39.errors.txt
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
tests/cases/conformance/es6/for-ofStatements/for-of39.ts(1,19): error TS2345: Argument of type '([string, number] | [string, true])[]' is not assignable to parameter of type 'ReadonlyArray<[string, boolean]>'.
Types of property 'concat' are incompatible.
Type '{ (...items: ConcatArray<[string, number] | [string, true]>[]): ([string, number] | [string, true])[]; (...items: ([string, number] | [string, true] | ConcatArray<[string, number] | [string, true]>)[]): ([string, number] | [string, true])[]; }' is not assignable to type '{ (...items: ConcatArray<[string, boolean]>[]): [string, boolean][]; (...items: ([string, boolean] | ConcatArray<[string, boolean]>)[]): [string, boolean][]; }'.
Types of parameters 'items' and 'items' are incompatible.
Type 'ConcatArray<[string, boolean]>' is not assignable to type 'ConcatArray<[string, number] | [string, true]>'.
Type '[string, boolean]' is not assignable to type '[string, number] | [string, true]'.
Type '[string, boolean]' is not assignable to type '[string, number]'.
Type 'boolean' is not assignable to type 'number'.
Type '[string, number] | [string, true]' is not assignable to type '[string, boolean]'.
Type '[string, number]' is not assignable to type '[string, boolean]'.
Type 'number' is not assignable to type 'boolean'.


==== tests/cases/conformance/es6/for-ofStatements/for-of39.ts (1 errors) ====
var map = new Map([["", true], ["", 0]]);
~~~~~~~~~~~~~~~~~~~~~
!!! error TS2345: Argument of type '([string, number] | [string, true])[]' is not assignable to parameter of type 'ReadonlyArray<[string, boolean]>'.
!!! error TS2345: Types of property 'concat' are incompatible.
!!! error TS2345: Type '{ (...items: ConcatArray<[string, number] | [string, true]>[]): ([string, number] | [string, true])[]; (...items: ([string, number] | [string, true] | ConcatArray<[string, number] | [string, true]>)[]): ([string, number] | [string, true])[]; }' is not assignable to type '{ (...items: ConcatArray<[string, boolean]>[]): [string, boolean][]; (...items: ([string, boolean] | ConcatArray<[string, boolean]>)[]): [string, boolean][]; }'.
!!! error TS2345: Types of parameters 'items' and 'items' are incompatible.
!!! error TS2345: Type 'ConcatArray<[string, boolean]>' is not assignable to type 'ConcatArray<[string, number] | [string, true]>'.
!!! error TS2345: Type '[string, boolean]' is not assignable to type '[string, number] | [string, true]'.
!!! error TS2345: Type '[string, boolean]' is not assignable to type '[string, number]'.
!!! error TS2345: Type 'boolean' is not assignable to type 'number'.
!!! error TS2345: Type '[string, number] | [string, true]' is not assignable to type '[string, boolean]'.
!!! error TS2345: Type '[string, number]' is not assignable to type '[string, boolean]'.
!!! error TS2345: Type 'number' is not assignable to type 'boolean'.
for (var [k, v] of map) {
k;
v;
Expand Down
18 changes: 17 additions & 1 deletion tests/baselines/reference/infiniteConstraints.errors.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
error TS2321: Excessive stack depth comparing types 'Extract<T[Exclude<keyof T, string | number | symbol>], Record<"val", string>>["val"]' and 'Extract<T[Exclude<keyof T, Exclude<keyof T, string | number | symbol>>], Record<"val", string>>["val"]'.
tests/cases/compiler/infiniteConstraints.ts(4,37): error TS2536: Type '"val"' cannot be used to index type 'B[Exclude<keyof B, K>]'.
tests/cases/compiler/infiniteConstraints.ts(27,36): error TS2345: Argument of type '{ main: Record<"val", "test">; alternate: Record<"val", "test2">; }' is not assignable to parameter of type '{ main: never; alternate: never; }'.
Types of property 'main' are incompatible.
Type 'Record<"val", "test">' is not assignable to type 'never'.
tests/cases/compiler/infiniteConstraints.ts(29,44): error TS2345: Argument of type '{ main: Record<"val", "test">; }' is not assignable to parameter of type '{ main: never; }'.
Types of property 'main' are incompatible.
Type 'Record<"val", "test">' is not assignable to type 'never'.
tests/cases/compiler/infiniteConstraints.ts(31,42): error TS2345: Argument of type '{ main: Record<"val", "dup">; alternate: Record<"val", "dup">; }' is not assignable to parameter of type '{ main: never; alternate: never; }'.
Types of property 'main' are incompatible.
Type 'Record<"val", "dup">' is not assignable to type 'never'.
tests/cases/compiler/infiniteConstraints.ts(36,71): error TS2536: Type '"foo"' cannot be used to index type 'T[keyof T]'.


==== tests/cases/compiler/infiniteConstraints.ts (3 errors) ====
!!! error TS2321: Excessive stack depth comparing types 'Extract<T[Exclude<keyof T, string | number | symbol>], Record<"val", string>>["val"]' and 'Extract<T[Exclude<keyof T, Exclude<keyof T, string | number | symbol>>], Record<"val", string>>["val"]'.
==== tests/cases/compiler/infiniteConstraints.ts (5 errors) ====
// Both of the following types trigger the recursion limiter in getImmediateBaseConstraint

type T1<B extends { [K in keyof B]: Extract<B[Exclude<keyof B, K>], { val: string }>["val"] }> = B;
Expand Down Expand Up @@ -35,8 +43,16 @@ tests/cases/compiler/infiniteConstraints.ts(36,71): error TS2536: Type '"foo"' c
>(vals: T): void;

const noError = ensureNoDuplicates({main: value("test"), alternate: value("test2")});
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2345: Argument of type '{ main: Record<"val", "test">; alternate: Record<"val", "test2">; }' is not assignable to parameter of type '{ main: never; alternate: never; }'.
!!! error TS2345: Types of property 'main' are incompatible.
!!! error TS2345: Type 'Record<"val", "test">' is not assignable to type 'never'.

const shouldBeNoError = ensureNoDuplicates({main: value("test")});
~~~~~~~~~~~~~~~~~~~~~
!!! error TS2345: Argument of type '{ main: Record<"val", "test">; }' is not assignable to parameter of type '{ main: never; }'.
!!! error TS2345: Types of property 'main' are incompatible.
!!! error TS2345: Type 'Record<"val", "test">' is not assignable to type 'never'.

const shouldBeError = ensureNoDuplicates({main: value("dup"), alternate: value("dup")});
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
16 changes: 8 additions & 8 deletions tests/baselines/reference/infiniteConstraints.types
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ declare function value<V extends string>(val: V): Value<V>;
>val : V

declare function ensureNoDuplicates<
>ensureNoDuplicates : <T extends { [K in keyof T]: Extract<T[K], Record<"val", string>>["val"] extends Extract<T[Exclude<keyof T, K>], Record<"val", string>>["val"] ? never : any; }>(vals: T) => void
>ensureNoDuplicates : <T extends { [K in keyof T]: never; }>(vals: T) => void

T extends {
[K in keyof T]: Extract<T[K], Value>["val"] extends Extract<T[Exclude<keyof T, K>], Value>["val"]
Expand All @@ -50,9 +50,9 @@ declare function ensureNoDuplicates<
>vals : T

const noError = ensureNoDuplicates({main: value("test"), alternate: value("test2")});
>noError : void
>ensureNoDuplicates({main: value("test"), alternate: value("test2")}) : void
>ensureNoDuplicates : <T extends { [K in keyof T]: Extract<T[K], Record<"val", string>>["val"] extends Extract<T[Exclude<keyof T, K>], Record<"val", string>>["val"] ? never : any; }>(vals: T) => void
>noError : any
>ensureNoDuplicates({main: value("test"), alternate: value("test2")}) : any
>ensureNoDuplicates : <T extends { [K in keyof T]: never; }>(vals: T) => void
>{main: value("test"), alternate: value("test2")} : { main: Record<"val", "test">; alternate: Record<"val", "test2">; }
>main : Record<"val", "test">
>value("test") : Record<"val", "test">
Expand All @@ -64,9 +64,9 @@ const noError = ensureNoDuplicates({main: value("test"), alternate: value("test2
>"test2" : "test2"

const shouldBeNoError = ensureNoDuplicates({main: value("test")});
>shouldBeNoError : void
>ensureNoDuplicates({main: value("test")}) : void
>ensureNoDuplicates : <T extends { [K in keyof T]: Extract<T[K], Record<"val", string>>["val"] extends Extract<T[Exclude<keyof T, K>], Record<"val", string>>["val"] ? never : any; }>(vals: T) => void
>shouldBeNoError : any
>ensureNoDuplicates({main: value("test")}) : any
>ensureNoDuplicates : <T extends { [K in keyof T]: never; }>(vals: T) => void
>{main: value("test")} : { main: Record<"val", "test">; }
>main : Record<"val", "test">
>value("test") : Record<"val", "test">
Expand All @@ -76,7 +76,7 @@ const shouldBeNoError = ensureNoDuplicates({main: value("test")});
const shouldBeError = ensureNoDuplicates({main: value("dup"), alternate: value("dup")});
>shouldBeError : any
>ensureNoDuplicates({main: value("dup"), alternate: value("dup")}) : any
>ensureNoDuplicates : <T extends { [K in keyof T]: Extract<T[K], Record<"val", string>>["val"] extends Extract<T[Exclude<keyof T, K>], Record<"val", string>>["val"] ? never : any; }>(vals: T) => void
>ensureNoDuplicates : <T extends { [K in keyof T]: never; }>(vals: T) => void
>{main: value("dup"), alternate: value("dup")} : { main: Record<"val", "dup">; alternate: Record<"val", "dup">; }
>main : Record<"val", "dup">
>value("dup") : Record<"val", "dup">
Expand Down
Loading