diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 09354d957a36e..1fd70a22b677a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7440,9 +7440,12 @@ namespace ts { return false; } - function isSubtypeOfAny(candidate: Type, types: Type[]): boolean { - for (const type of types) { - if (candidate !== type && isTypeSubtypeOf(candidate, type)) { + function isSubtypeOfAny(source: Type, targets: Type[]): boolean { + for (const target of targets) { + if (source !== target && isTypeSubtypeOf(source, target) && ( + !(getObjectFlags(getTargetType(source)) & ObjectFlags.Class) || + !(getObjectFlags(getTargetType(target)) & ObjectFlags.Class) || + isTypeDerivedFrom(source, target))) { return true; } } @@ -8565,12 +8568,19 @@ namespace ts { return isTypeRelatedTo(source, target, assignableRelation); } - // A type S is considered to be an instance of a type T if S and T are the same type or if S is a - // subtype of T but not structurally identical to T. This specifically means that two distinct but - // structurally identical types (such as two classes) are not considered instances of each other. - function isTypeInstanceOf(source: Type, target: Type): boolean { - return getTargetType(source) === getTargetType(target) || isTypeSubtypeOf(source, target) && !isTypeIdenticalTo(source, target); - } + // An object type S is considered to be derived from an object type T if + // S is a union type and every constituent of S is derived from T, + // T is a union type and S is derived from at least one constituent of T, or + // T is one of the global types Object and Function and S is a subtype of T, or + // T occurs directly or indirectly in an 'extends' clause of S. + // Note that this check ignores type parameters and only considers the + // inheritance hierarchy. + function isTypeDerivedFrom(source: Type, target: Type): boolean { + return source.flags & TypeFlags.Union ? every((source).types, t => isTypeDerivedFrom(t, target)) : + target.flags & TypeFlags.Union ? some((target).types, t => isTypeDerivedFrom(source, t)) : + target === globalObjectType || target === globalFunctionType ? isTypeSubtypeOf(source, target) : + hasBaseType(source, getTargetType(target)); + } /** * This is *not* a bi-directional relationship. @@ -9604,7 +9614,7 @@ namespace ts { if (relation === identityRelation) { return propertiesIdenticalTo(source, target); } - const requireOptionalProperties = relation === subtypeRelation && !isObjectLiteralType(source); + const requireOptionalProperties = relation === subtypeRelation && !isObjectLiteralType(source) && !isEmptyArrayLiteralType(source); const unmatchedProperty = getUnmatchedProperty(source, target, requireOptionalProperties); if (unmatchedProperty) { if (reportErrors) { @@ -10312,6 +10322,11 @@ namespace ts { !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType); } + function isEmptyArrayLiteralType(type: Type): boolean { + const elementType = isArrayType(type) ? (type).typeArguments[0] : undefined; + return elementType === undefinedWideningType || elementType === neverType; + } + function isTupleLikeType(type: Type): boolean { return !!getPropertyOfType(type, "0" as __String); } @@ -12415,7 +12430,7 @@ namespace ts { } if (targetType) { - return getNarrowedType(type, targetType, assumeTrue, isTypeInstanceOf); + return getNarrowedType(type, targetType, assumeTrue, isTypeDerivedFrom); } return type; @@ -13883,7 +13898,6 @@ namespace ts { type.pattern = node; return type; } - const contextualType = getApparentTypeOfContextualType(node); if (contextualType && contextualTypeIsTupleLikeType(contextualType)) { const pattern = contextualType.pattern; // If array literal is contextually typed by a binding pattern or an assignment pattern, pad the resulting @@ -18087,14 +18101,6 @@ namespace ts { return (target.flags & TypeFlags.Nullable) !== 0 || isTypeComparableTo(source, target); } - function getBestChoiceType(type1: Type, type2: Type): Type { - const firstAssignableToSecond = isTypeAssignableTo(type1, type2); - const secondAssignableToFirst = isTypeAssignableTo(type2, type1); - return secondAssignableToFirst && !firstAssignableToSecond ? type1 : - firstAssignableToSecond && !secondAssignableToFirst ? type2 : - getUnionType([type1, type2], /*subtypeReduction*/ true); - } - function checkBinaryExpression(node: BinaryExpression, checkMode?: CheckMode) { return checkBinaryLikeExpression(node.left, node.operatorToken, node.right, checkMode, node); } @@ -18231,7 +18237,7 @@ namespace ts { leftType; case SyntaxKind.BarBarToken: return getTypeFacts(leftType) & TypeFacts.Falsy ? - getBestChoiceType(removeDefinitelyFalsyTypes(leftType), rightType) : + getUnionType([removeDefinitelyFalsyTypes(leftType), rightType], /*subtypeReduction*/ true) : leftType; case SyntaxKind.EqualsToken: checkAssignmentOperator(rightType); @@ -18391,7 +18397,7 @@ namespace ts { checkExpression(node.condition); const type1 = checkExpression(node.whenTrue, checkMode); const type2 = checkExpression(node.whenFalse, checkMode); - return getBestChoiceType(type1, type2); + return getUnionType([type1, type2], /*subtypeReduction*/ true); } function checkTemplateExpression(node: TemplateExpression): Type { diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index fe5482cf5934b..d16903bc30054 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2641,7 +2641,7 @@ namespace ts { /** * Gets a custom text range to use when emitting source maps. */ - export function getSourceMapRange(node: Node) { + export function getSourceMapRange(node: Node): SourceMapRange { const emitNode = node.emitNode; return (emitNode && emitNode.sourceMapRange) || node; } diff --git a/tests/baselines/reference/arrayLiteralsWithRecursiveGenerics.types b/tests/baselines/reference/arrayLiteralsWithRecursiveGenerics.types index 9cdbe0a0a6157..75013cdef098e 100644 --- a/tests/baselines/reference/arrayLiteralsWithRecursiveGenerics.types +++ b/tests/baselines/reference/arrayLiteralsWithRecursiveGenerics.types @@ -55,8 +55,8 @@ var myList: MyList; >MyList : MyList var xs = [list, myList]; // {}[] ->xs : List[] ->[list, myList] : List[] +>xs : (List | MyList)[] +>[list, myList] : (List | MyList)[] >list : List >myList : MyList diff --git a/tests/baselines/reference/fixSignatureCaching.types b/tests/baselines/reference/fixSignatureCaching.types index 6f87f563707b0..781912544e25d 100644 --- a/tests/baselines/reference/fixSignatureCaching.types +++ b/tests/baselines/reference/fixSignatureCaching.types @@ -866,9 +866,9 @@ define(function () { >'UnknownMobile' : "UnknownMobile" isArray = ('isArray' in Array) ? ->isArray = ('isArray' in Array) ? Array.isArray : function (value) { return Object.prototype.toString.call(value) === '[object Array]'; } : (value: any) => boolean +>isArray = ('isArray' in Array) ? Array.isArray : function (value) { return Object.prototype.toString.call(value) === '[object Array]'; } : (arg: any) => arg is any[] >isArray : any ->('isArray' in Array) ? Array.isArray : function (value) { return Object.prototype.toString.call(value) === '[object Array]'; } : (value: any) => boolean +>('isArray' in Array) ? Array.isArray : function (value) { return Object.prototype.toString.call(value) === '[object Array]'; } : (arg: any) => arg is any[] >('isArray' in Array) : boolean >'isArray' in Array : boolean >'isArray' : "isArray" diff --git a/tests/baselines/reference/narrowingGenericTypeFromInstanceof01.errors.txt b/tests/baselines/reference/narrowingGenericTypeFromInstanceof01.errors.txt index c686cd7a7cffc..bb35b351c1b44 100644 --- a/tests/baselines/reference/narrowingGenericTypeFromInstanceof01.errors.txt +++ b/tests/baselines/reference/narrowingGenericTypeFromInstanceof01.errors.txt @@ -1,6 +1,5 @@ -tests/cases/conformance/types/typeRelationships/instanceOf/narrowingGenericTypeFromInstanceof01.ts(13,17): error TS2345: Argument of type 'A | B' is not assignable to parameter of type 'A'. - Type 'B' is not assignable to type 'A'. - Property 'a' is missing in type 'B'. +tests/cases/conformance/types/typeRelationships/instanceOf/narrowingGenericTypeFromInstanceof01.ts(13,17): error TS2345: Argument of type 'B' is not assignable to parameter of type 'A<{}>'. + Property 'a' is missing in type 'B'. ==== tests/cases/conformance/types/typeRelationships/instanceOf/narrowingGenericTypeFromInstanceof01.ts (1 errors) ==== @@ -18,9 +17,8 @@ tests/cases/conformance/types/typeRelationships/instanceOf/narrowingGenericTypeF if (x instanceof B) { acceptA(x); ~ -!!! error TS2345: Argument of type 'A | B' is not assignable to parameter of type 'A'. -!!! error TS2345: Type 'B' is not assignable to type 'A'. -!!! error TS2345: Property 'a' is missing in type 'B'. +!!! error TS2345: Argument of type 'B' is not assignable to parameter of type 'A<{}>'. +!!! error TS2345: Property 'a' is missing in type 'B'. } if (x instanceof A) { diff --git a/tests/baselines/reference/narrowingGenericTypeFromInstanceof01.types b/tests/baselines/reference/narrowingGenericTypeFromInstanceof01.types index 22d02d17ce12e..964c7920a3432 100644 --- a/tests/baselines/reference/narrowingGenericTypeFromInstanceof01.types +++ b/tests/baselines/reference/narrowingGenericTypeFromInstanceof01.types @@ -43,7 +43,7 @@ function test(x: A | B) { acceptA(x); >acceptA(x) : any >acceptA : (a: A) => void ->x : A | B +>x : B } if (x instanceof A) { @@ -65,7 +65,7 @@ function test(x: A | B) { acceptB(x); >acceptB(x) : void >acceptB : (b: B) => void ->x : A | B +>x : B } if (x instanceof B) { @@ -76,6 +76,6 @@ function test(x: A | B) { acceptB(x); >acceptB(x) : void >acceptB : (b: B) => void ->x : A | B +>x : B } } diff --git a/tests/baselines/reference/nonContextuallyTypedLogicalOr.symbols b/tests/baselines/reference/nonContextuallyTypedLogicalOr.symbols index 43d7539b6b9b6..2d921f6631c23 100644 --- a/tests/baselines/reference/nonContextuallyTypedLogicalOr.symbols +++ b/tests/baselines/reference/nonContextuallyTypedLogicalOr.symbols @@ -28,8 +28,8 @@ var e: Ellement; >Ellement : Symbol(Ellement, Decl(nonContextuallyTypedLogicalOr.ts, 3, 1)) (c || e).dummy; ->(c || e).dummy : Symbol(Contextual.dummy, Decl(nonContextuallyTypedLogicalOr.ts, 0, 22)) +>(c || e).dummy : Symbol(dummy, Decl(nonContextuallyTypedLogicalOr.ts, 0, 22), Decl(nonContextuallyTypedLogicalOr.ts, 5, 20)) >c : Symbol(c, Decl(nonContextuallyTypedLogicalOr.ts, 10, 3)) >e : Symbol(e, Decl(nonContextuallyTypedLogicalOr.ts, 11, 3)) ->dummy : Symbol(Contextual.dummy, Decl(nonContextuallyTypedLogicalOr.ts, 0, 22)) +>dummy : Symbol(dummy, Decl(nonContextuallyTypedLogicalOr.ts, 0, 22), Decl(nonContextuallyTypedLogicalOr.ts, 5, 20)) diff --git a/tests/baselines/reference/nonContextuallyTypedLogicalOr.types b/tests/baselines/reference/nonContextuallyTypedLogicalOr.types index 5061ad1d9758b..aeb9d1409c6e6 100644 --- a/tests/baselines/reference/nonContextuallyTypedLogicalOr.types +++ b/tests/baselines/reference/nonContextuallyTypedLogicalOr.types @@ -29,8 +29,8 @@ var e: Ellement; (c || e).dummy; >(c || e).dummy : any ->(c || e) : Contextual ->c || e : Contextual +>(c || e) : Contextual | Ellement +>c || e : Contextual | Ellement >c : Contextual >e : Ellement >dummy : any