Merge pull request #26790 from Microsoft/fixWeakObjectRelationCheck

Fix weak object relation check
This commit is contained in:
Anders Hejlsberg
2018-08-30 22:08:44 -07:00
committed by GitHub
8 changed files with 87 additions and 103 deletions

View File

@@ -11190,13 +11190,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);
@@ -11841,6 +11838,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
@@ -12020,34 +12020,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;
@@ -12292,6 +12264,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) {

View File

@@ -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) ====
@@ -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>
@@ -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'.

View File

@@ -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 '{}[]'.
@@ -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 '[{}]'.

View File

@@ -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;

View File

@@ -1,10 +1,15 @@
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,37): error TS2322: Type 'Record<"val", "test">' is not assignable to type 'never'.
tests/cases/compiler/infiniteConstraints.ts(27,58): error TS2322: Type 'Record<"val", "test2">' is not assignable to type 'never'.
tests/cases/compiler/infiniteConstraints.ts(29,45): error TS2322: Type 'Record<"val", "test">' is not assignable to type 'never'.
tests/cases/compiler/infiniteConstraints.ts(31,43): error TS2322: Type 'Record<"val", "dup">' is not assignable to type 'never'.
tests/cases/compiler/infiniteConstraints.ts(31,63): error TS2322: 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 (4 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 (7 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;
@@ -34,8 +39,17 @@ 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 TS2322: Type 'Record<"val", "test">' is not assignable to type 'never'.
!!! related TS6500 tests/cases/compiler/infiniteConstraints.ts:27:37: The expected type comes from property 'main' which is declared here on type '{ main: never; alternate: never; }'
~~~~~~~~~
!!! error TS2322: Type 'Record<"val", "test2">' is not assignable to type 'never'.
!!! related TS6500 tests/cases/compiler/infiniteConstraints.ts:27:58: The expected type comes from property 'alternate' which is declared here on type '{ main: never; alternate: never; }'
const shouldBeNoError = ensureNoDuplicates({main: value("test")});
~~~~
!!! error TS2322: Type 'Record<"val", "test">' is not assignable to type 'never'.
!!! related TS6500 tests/cases/compiler/infiniteConstraints.ts:29:45: The expected type comes from property 'main' which is declared here on type '{ main: never; }'
const shouldBeError = ensureNoDuplicates({main: value("dup"), alternate: value("dup")});
~~~~

View File

@@ -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"]
@@ -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">
@@ -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">
@@ -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">

View File

@@ -1,11 +1,8 @@
tests/cases/conformance/es6/destructuring/iterableArrayPattern28.ts(1,33): error TS2501: A rest element cannot contain a binding pattern.
tests/cases/conformance/es6/destructuring/iterableArrayPattern28.ts(2,32): error TS2345: Argument of type '([string, number] | [string, boolean])[]' is not assignable to parameter of type 'ReadonlyArray<[string, number]>'.
Types of property 'concat' are incompatible.
Type '{ (...items: ConcatArray<[string, number] | [string, boolean]>[]): ([string, number] | [string, boolean])[]; (...items: ([string, number] | [string, boolean] | ConcatArray<[string, number] | [string, boolean]>)[]): ([string, number] | [string, boolean])[]; }' is not assignable to type '{ (...items: ConcatArray<[string, number]>[]): [string, number][]; (...items: ([string, number] | ConcatArray<[string, number]>)[]): [string, number][]; }'.
Type '([string, number] | [string, boolean])[]' is not assignable to type '[string, number][]'.
Type '[string, number] | [string, boolean]' is not assignable to type '[string, number]'.
Type '[string, boolean]' is not assignable to type '[string, number]'.
Type 'boolean' is not assignable to type 'number'.
Type '[string, number] | [string, boolean]' is not assignable to type '[string, number]'.
Type '[string, boolean]' is not assignable to type '[string, number]'.
Type 'boolean' is not assignable to type 'number'.
==== tests/cases/conformance/es6/destructuring/iterableArrayPattern28.ts (2 errors) ====
@@ -15,9 +12,6 @@ tests/cases/conformance/es6/destructuring/iterableArrayPattern28.ts(2,32): error
takeFirstTwoEntries(...new Map([["", 0], ["hello", true]]));
~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2345: Argument of type '([string, number] | [string, boolean])[]' is not assignable to parameter of type 'ReadonlyArray<[string, number]>'.
!!! error TS2345: Types of property 'concat' are incompatible.
!!! error TS2345: Type '{ (...items: ConcatArray<[string, number] | [string, boolean]>[]): ([string, number] | [string, boolean])[]; (...items: ([string, number] | [string, boolean] | ConcatArray<[string, number] | [string, boolean]>)[]): ([string, number] | [string, boolean])[]; }' is not assignable to type '{ (...items: ConcatArray<[string, number]>[]): [string, number][]; (...items: ([string, number] | ConcatArray<[string, number]>)[]): [string, number][]; }'.
!!! error TS2345: Type '([string, number] | [string, boolean])[]' is not assignable to type '[string, number][]'.
!!! error TS2345: Type '[string, number] | [string, boolean]' is not assignable to type '[string, number]'.
!!! 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, boolean]' is not assignable to type '[string, number]'.
!!! error TS2345: Type '[string, boolean]' is not assignable to type '[string, number]'.
!!! error TS2345: Type 'boolean' is not assignable to type 'number'.

View File

@@ -9,15 +9,11 @@ tests/cases/compiler/tupleTypes.ts(18,1): error TS2322: Type '[number, string, n
Type '3' is not assignable to type '2'.
tests/cases/compiler/tupleTypes.ts(41,1): error TS2322: Type '[]' is not assignable to type '[number, string]'.
tests/cases/compiler/tupleTypes.ts(47,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/compiler/tupleTypes.ts(49,1): error TS2322: Type '[number, {}]' is not assignable to type 'number[]'.
Types of property 'pop' are incompatible.
Type '() => number | {}' is not assignable to type '() => number'.
Type 'number | {}' is not assignable to type 'number'.
Type '{}' is not assignable to type 'number'.
Type 'number | {}' is not assignable to type 'number'.
Type '{}' is not assignable to type 'number'.
tests/cases/compiler/tupleTypes.ts(50,1): error TS2322: Type '[number, number]' is not assignable to type '[number, string]'.
Type 'number' is not assignable to type 'string'.
tests/cases/compiler/tupleTypes.ts(51,1): error TS2322: Type '[number, {}]' is not assignable to type '[number, string]'.
@@ -90,18 +86,14 @@ tests/cases/compiler/tupleTypes.ts(51,1): error TS2322: Type '[number, {}]' is n
a = a1; // Error
~
!!! 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'.
a = a2;
a = a3; // Error
~
!!! error TS2322: Type '[number, {}]' is not assignable to type 'number[]'.
!!! error TS2322: Types of property 'pop' are incompatible.
!!! error TS2322: Type '() => number | {}' is not assignable to type '() => number'.
!!! error TS2322: Type 'number | {}' is not assignable to type 'number'.
!!! error TS2322: Type '{}' is not assignable to type 'number'.
!!! error TS2322: Type 'number | {}' is not assignable to type 'number'.
!!! error TS2322: Type '{}' is not assignable to type 'number'.
a1 = a2; // Error
~~
!!! error TS2322: Type '[number, number]' is not assignable to type '[number, string]'.