diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 92a989a2bc707..09b9b57f41c3d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18759,7 +18759,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // 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 - // S is a type variable with a base constraint that is derived from T, + // S is an intersection type and some constituent of S is derived from T, or + // S is a type variable with a base constraint that is derived from T, or + // T is {} and S is an object-like type (ensuring {} is less derived than Object), 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 @@ -18767,8 +18769,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function isTypeDerivedFrom(source: Type, target: Type): boolean { return source.flags & TypeFlags.Union ? every((source as UnionType).types, t => isTypeDerivedFrom(t, target)) : target.flags & TypeFlags.Union ? some((target as UnionType).types, t => isTypeDerivedFrom(source, t)) : + source.flags & TypeFlags.Intersection ? some((source as IntersectionType).types, t => isTypeDerivedFrom(t, target)) : source.flags & TypeFlags.InstantiableNonPrimitive ? isTypeDerivedFrom(getBaseConstraintOfType(source) || unknownType, target) : - target === globalObjectType ? !!(source.flags & (TypeFlags.Object | TypeFlags.NonPrimitive)) : + isEmptyAnonymousObjectType(target) ? !!(source.flags & (TypeFlags.Object | TypeFlags.NonPrimitive)) : + target === globalObjectType ? !!(source.flags & (TypeFlags.Object | TypeFlags.NonPrimitive)) && !isEmptyAnonymousObjectType(source) : target === globalFunctionType ? !!(source.flags & TypeFlags.Object) && isFunctionObjectType(source as ObjectType) : hasBaseType(source, getTargetType(target)) || (isArrayType(target) && !isReadonlyArrayType(target) && isTypeDerivedFrom(source, globalReadonlyArrayType)); } diff --git a/tests/baselines/reference/inKeywordAndIntersection.js b/tests/baselines/reference/inKeywordAndIntersection.js new file mode 100644 index 0000000000000..1fb275d24fc8e --- /dev/null +++ b/tests/baselines/reference/inKeywordAndIntersection.js @@ -0,0 +1,59 @@ +//// [inKeywordAndIntersection.ts] +class A { a = 0 } +class B { b = 0 } + +function f10(obj: A & { x: string } | B) { + if (obj instanceof Object) { + obj; // A & { x: string } | B + } + else { + obj; // Error + } +} + +// Repro from #50844 + +interface InstanceOne { + one(): void +} + +interface InstanceTwo { + two(): void +} + +const instance = {} as InstanceOne | InstanceTwo + +const ClassOne = {} as { new(): InstanceOne } & { foo: true }; + +if (instance instanceof ClassOne) { + instance.one(); +} + + +//// [inKeywordAndIntersection.js] +"use strict"; +var A = /** @class */ (function () { + function A() { + this.a = 0; + } + return A; +}()); +var B = /** @class */ (function () { + function B() { + this.b = 0; + } + return B; +}()); +function f10(obj) { + if (obj instanceof Object) { + obj; // A & { x: string } | B + } + else { + obj; // Error + } +} +var instance = {}; +var ClassOne = {}; +if (instance instanceof ClassOne) { + instance.one(); +} diff --git a/tests/baselines/reference/inKeywordAndIntersection.symbols b/tests/baselines/reference/inKeywordAndIntersection.symbols new file mode 100644 index 0000000000000..44e75202c87ec --- /dev/null +++ b/tests/baselines/reference/inKeywordAndIntersection.symbols @@ -0,0 +1,65 @@ +=== tests/cases/compiler/inKeywordAndIntersection.ts === +class A { a = 0 } +>A : Symbol(A, Decl(inKeywordAndIntersection.ts, 0, 0)) +>a : Symbol(A.a, Decl(inKeywordAndIntersection.ts, 0, 9)) + +class B { b = 0 } +>B : Symbol(B, Decl(inKeywordAndIntersection.ts, 0, 17)) +>b : Symbol(B.b, Decl(inKeywordAndIntersection.ts, 1, 9)) + +function f10(obj: A & { x: string } | B) { +>f10 : Symbol(f10, Decl(inKeywordAndIntersection.ts, 1, 17)) +>obj : Symbol(obj, Decl(inKeywordAndIntersection.ts, 3, 13)) +>A : Symbol(A, Decl(inKeywordAndIntersection.ts, 0, 0)) +>x : Symbol(x, Decl(inKeywordAndIntersection.ts, 3, 23)) +>B : Symbol(B, Decl(inKeywordAndIntersection.ts, 0, 17)) + + if (obj instanceof Object) { +>obj : Symbol(obj, Decl(inKeywordAndIntersection.ts, 3, 13)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + obj; // A & { x: string } | B +>obj : Symbol(obj, Decl(inKeywordAndIntersection.ts, 3, 13)) + } + else { + obj; // Error +>obj : Symbol(obj, Decl(inKeywordAndIntersection.ts, 3, 13)) + } +} + +// Repro from #50844 + +interface InstanceOne { +>InstanceOne : Symbol(InstanceOne, Decl(inKeywordAndIntersection.ts, 10, 1)) + + one(): void +>one : Symbol(InstanceOne.one, Decl(inKeywordAndIntersection.ts, 14, 23)) +} + +interface InstanceTwo { +>InstanceTwo : Symbol(InstanceTwo, Decl(inKeywordAndIntersection.ts, 16, 1)) + + two(): void +>two : Symbol(InstanceTwo.two, Decl(inKeywordAndIntersection.ts, 18, 23)) +} + +const instance = {} as InstanceOne | InstanceTwo +>instance : Symbol(instance, Decl(inKeywordAndIntersection.ts, 22, 5)) +>InstanceOne : Symbol(InstanceOne, Decl(inKeywordAndIntersection.ts, 10, 1)) +>InstanceTwo : Symbol(InstanceTwo, Decl(inKeywordAndIntersection.ts, 16, 1)) + +const ClassOne = {} as { new(): InstanceOne } & { foo: true }; +>ClassOne : Symbol(ClassOne, Decl(inKeywordAndIntersection.ts, 24, 5)) +>InstanceOne : Symbol(InstanceOne, Decl(inKeywordAndIntersection.ts, 10, 1)) +>foo : Symbol(foo, Decl(inKeywordAndIntersection.ts, 24, 49)) + +if (instance instanceof ClassOne) { +>instance : Symbol(instance, Decl(inKeywordAndIntersection.ts, 22, 5)) +>ClassOne : Symbol(ClassOne, Decl(inKeywordAndIntersection.ts, 24, 5)) + + instance.one(); +>instance.one : Symbol(InstanceOne.one, Decl(inKeywordAndIntersection.ts, 14, 23)) +>instance : Symbol(instance, Decl(inKeywordAndIntersection.ts, 22, 5)) +>one : Symbol(InstanceOne.one, Decl(inKeywordAndIntersection.ts, 14, 23)) +} + diff --git a/tests/baselines/reference/inKeywordAndIntersection.types b/tests/baselines/reference/inKeywordAndIntersection.types new file mode 100644 index 0000000000000..7fc2b950501f7 --- /dev/null +++ b/tests/baselines/reference/inKeywordAndIntersection.types @@ -0,0 +1,66 @@ +=== tests/cases/compiler/inKeywordAndIntersection.ts === +class A { a = 0 } +>A : A +>a : number +>0 : 0 + +class B { b = 0 } +>B : B +>b : number +>0 : 0 + +function f10(obj: A & { x: string } | B) { +>f10 : (obj: (A & { x: string;}) | B) => void +>obj : B | (A & { x: string; }) +>x : string + + if (obj instanceof Object) { +>obj instanceof Object : boolean +>obj : B | (A & { x: string; }) +>Object : ObjectConstructor + + obj; // A & { x: string } | B +>obj : B | (A & { x: string; }) + } + else { + obj; // Error +>obj : never + } +} + +// Repro from #50844 + +interface InstanceOne { + one(): void +>one : () => void +} + +interface InstanceTwo { + two(): void +>two : () => void +} + +const instance = {} as InstanceOne | InstanceTwo +>instance : InstanceOne | InstanceTwo +>{} as InstanceOne | InstanceTwo : InstanceOne | InstanceTwo +>{} : {} + +const ClassOne = {} as { new(): InstanceOne } & { foo: true }; +>ClassOne : (new () => InstanceOne) & { foo: true; } +>{} as { new(): InstanceOne } & { foo: true } : (new () => InstanceOne) & { foo: true; } +>{} : {} +>foo : true +>true : true + +if (instance instanceof ClassOne) { +>instance instanceof ClassOne : boolean +>instance : InstanceOne | InstanceTwo +>ClassOne : (new () => InstanceOne) & { foo: true; } + + instance.one(); +>instance.one() : void +>instance.one : () => void +>instance : InstanceOne +>one : () => void +} + diff --git a/tests/baselines/reference/inKeywordAndUnknown.errors.txt b/tests/baselines/reference/inKeywordAndUnknown.errors.txt index 61c2cd2a2a364..59492364713ef 100644 --- a/tests/baselines/reference/inKeywordAndUnknown.errors.txt +++ b/tests/baselines/reference/inKeywordAndUnknown.errors.txt @@ -20,4 +20,38 @@ tests/cases/compiler/inKeywordAndUnknown.ts(12,18): error TS2638: Type '{}' may } y; // {} } + + // Repro from #51007 + + function isHTMLTable(table: unknown): boolean { + return !!table && table instanceof Object && 'html' in table; + } + + function f1(x: unknown) { + return x && x instanceof Object && 'a' in x; + } + + function f2(x: T) { + return x && x instanceof Object && 'a' in x; + } + + function f3(x: {}) { + return x instanceof Object && 'a' in x; + } + + function f4(x: T) { + return x instanceof Object && 'a' in x; + } + + function f5(x: T & {}) { + return x instanceof Object && 'a' in x; + } + + function f6(x: T & {}) { + return x instanceof Object && 'a' in x; + } + + function f7(x: T & {}) { + return x instanceof Object && 'a' in x; + } \ No newline at end of file diff --git a/tests/baselines/reference/inKeywordAndUnknown.js b/tests/baselines/reference/inKeywordAndUnknown.js index 354bb8c4b1884..10b11c1b99c02 100644 --- a/tests/baselines/reference/inKeywordAndUnknown.js +++ b/tests/baselines/reference/inKeywordAndUnknown.js @@ -15,6 +15,40 @@ function f(x: {}, y: unknown) { } y; // {} } + +// Repro from #51007 + +function isHTMLTable(table: unknown): boolean { + return !!table && table instanceof Object && 'html' in table; +} + +function f1(x: unknown) { + return x && x instanceof Object && 'a' in x; +} + +function f2(x: T) { + return x && x instanceof Object && 'a' in x; +} + +function f3(x: {}) { + return x instanceof Object && 'a' in x; +} + +function f4(x: T) { + return x instanceof Object && 'a' in x; +} + +function f5(x: T & {}) { + return x instanceof Object && 'a' in x; +} + +function f6(x: T & {}) { + return x instanceof Object && 'a' in x; +} + +function f7(x: T & {}) { + return x instanceof Object && 'a' in x; +} //// [inKeywordAndUnknown.js] @@ -34,3 +68,28 @@ function f(x, y) { } y; // {} } +// Repro from #51007 +function isHTMLTable(table) { + return !!table && table instanceof Object && 'html' in table; +} +function f1(x) { + return x && x instanceof Object && 'a' in x; +} +function f2(x) { + return x && x instanceof Object && 'a' in x; +} +function f3(x) { + return x instanceof Object && 'a' in x; +} +function f4(x) { + return x instanceof Object && 'a' in x; +} +function f5(x) { + return x instanceof Object && 'a' in x; +} +function f6(x) { + return x instanceof Object && 'a' in x; +} +function f7(x) { + return x instanceof Object && 'a' in x; +} diff --git a/tests/baselines/reference/inKeywordAndUnknown.symbols b/tests/baselines/reference/inKeywordAndUnknown.symbols index 145e92dc7f657..158c046b60044 100644 --- a/tests/baselines/reference/inKeywordAndUnknown.symbols +++ b/tests/baselines/reference/inKeywordAndUnknown.symbols @@ -31,3 +31,98 @@ function f(x: {}, y: unknown) { >y : Symbol(y, Decl(inKeywordAndUnknown.ts, 2, 17)) } +// Repro from #51007 + +function isHTMLTable(table: unknown): boolean { +>isHTMLTable : Symbol(isHTMLTable, Decl(inKeywordAndUnknown.ts, 15, 1)) +>table : Symbol(table, Decl(inKeywordAndUnknown.ts, 19, 21)) + + return !!table && table instanceof Object && 'html' in table; +>table : Symbol(table, Decl(inKeywordAndUnknown.ts, 19, 21)) +>table : Symbol(table, Decl(inKeywordAndUnknown.ts, 19, 21)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>table : Symbol(table, Decl(inKeywordAndUnknown.ts, 19, 21)) +} + +function f1(x: unknown) { +>f1 : Symbol(f1, Decl(inKeywordAndUnknown.ts, 21, 1)) +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 23, 12)) + + return x && x instanceof Object && 'a' in x; +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 23, 12)) +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 23, 12)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 23, 12)) +} + +function f2(x: T) { +>f2 : Symbol(f2, Decl(inKeywordAndUnknown.ts, 25, 1)) +>T : Symbol(T, Decl(inKeywordAndUnknown.ts, 27, 12)) +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 27, 15)) +>T : Symbol(T, Decl(inKeywordAndUnknown.ts, 27, 12)) + + return x && x instanceof Object && 'a' in x; +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 27, 15)) +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 27, 15)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 27, 15)) +} + +function f3(x: {}) { +>f3 : Symbol(f3, Decl(inKeywordAndUnknown.ts, 29, 1)) +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 31, 12)) + + return x instanceof Object && 'a' in x; +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 31, 12)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 31, 12)) +} + +function f4(x: T) { +>f4 : Symbol(f4, Decl(inKeywordAndUnknown.ts, 33, 1)) +>T : Symbol(T, Decl(inKeywordAndUnknown.ts, 35, 12)) +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 35, 26)) +>T : Symbol(T, Decl(inKeywordAndUnknown.ts, 35, 12)) + + return x instanceof Object && 'a' in x; +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 35, 26)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 35, 26)) +} + +function f5(x: T & {}) { +>f5 : Symbol(f5, Decl(inKeywordAndUnknown.ts, 37, 1)) +>T : Symbol(T, Decl(inKeywordAndUnknown.ts, 39, 12)) +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 39, 15)) +>T : Symbol(T, Decl(inKeywordAndUnknown.ts, 39, 12)) + + return x instanceof Object && 'a' in x; +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 39, 15)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 39, 15)) +} + +function f6(x: T & {}) { +>f6 : Symbol(f6, Decl(inKeywordAndUnknown.ts, 41, 1)) +>T : Symbol(T, Decl(inKeywordAndUnknown.ts, 43, 12)) +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 43, 26)) +>T : Symbol(T, Decl(inKeywordAndUnknown.ts, 43, 12)) + + return x instanceof Object && 'a' in x; +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 43, 26)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 43, 26)) +} + +function f7(x: T & {}) { +>f7 : Symbol(f7, Decl(inKeywordAndUnknown.ts, 45, 1)) +>T : Symbol(T, Decl(inKeywordAndUnknown.ts, 47, 12)) +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 47, 30)) +>T : Symbol(T, Decl(inKeywordAndUnknown.ts, 47, 12)) + + return x instanceof Object && 'a' in x; +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 47, 30)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(inKeywordAndUnknown.ts, 47, 30)) +} + diff --git a/tests/baselines/reference/inKeywordAndUnknown.types b/tests/baselines/reference/inKeywordAndUnknown.types index 59a587598b23d..359651a5dec24 100644 --- a/tests/baselines/reference/inKeywordAndUnknown.types +++ b/tests/baselines/reference/inKeywordAndUnknown.types @@ -40,3 +40,125 @@ function f(x: {}, y: unknown) { >y : Record<"a", unknown> } +// Repro from #51007 + +function isHTMLTable(table: unknown): boolean { +>isHTMLTable : (table: unknown) => boolean +>table : unknown + + return !!table && table instanceof Object && 'html' in table; +>!!table && table instanceof Object && 'html' in table : boolean +>!!table && table instanceof Object : boolean +>!!table : boolean +>!table : boolean +>table : unknown +>table instanceof Object : boolean +>table : {} +>Object : ObjectConstructor +>'html' in table : boolean +>'html' : "html" +>table : Object +} + +function f1(x: unknown) { +>f1 : (x: unknown) => unknown +>x : unknown + + return x && x instanceof Object && 'a' in x; +>x && x instanceof Object && 'a' in x : unknown +>x && x instanceof Object : unknown +>x : unknown +>x instanceof Object : boolean +>x : {} +>Object : ObjectConstructor +>'a' in x : boolean +>'a' : "a" +>x : Object +} + +function f2(x: T) { +>f2 : (x: T) => boolean +>x : T + + return x && x instanceof Object && 'a' in x; +>x && x instanceof Object && 'a' in x : boolean +>x && x instanceof Object : boolean +>x : T +>x instanceof Object : boolean +>x : NonNullable +>Object : ObjectConstructor +>'a' in x : boolean +>'a' : "a" +>x : T & Object +} + +function f3(x: {}) { +>f3 : (x: {}) => boolean +>x : {} + + return x instanceof Object && 'a' in x; +>x instanceof Object && 'a' in x : boolean +>x instanceof Object : boolean +>x : {} +>Object : ObjectConstructor +>'a' in x : boolean +>'a' : "a" +>x : Object +} + +function f4(x: T) { +>f4 : (x: T) => boolean +>x : T + + return x instanceof Object && 'a' in x; +>x instanceof Object && 'a' in x : boolean +>x instanceof Object : boolean +>x : T +>Object : ObjectConstructor +>'a' in x : boolean +>'a' : "a" +>x : T & Object +} + +function f5(x: T & {}) { +>f5 : (x: T & {}) => boolean +>x : T & {} + + return x instanceof Object && 'a' in x; +>x instanceof Object && 'a' in x : boolean +>x instanceof Object : boolean +>x : T & {} +>Object : ObjectConstructor +>'a' in x : boolean +>'a' : "a" +>x : T & Object +} + +function f6(x: T & {}) { +>f6 : (x: T & {}) => boolean +>x : T & {} + + return x instanceof Object && 'a' in x; +>x instanceof Object && 'a' in x : boolean +>x instanceof Object : boolean +>x : T & {} +>Object : ObjectConstructor +>'a' in x : boolean +>'a' : "a" +>x : T & Object +} + +function f7(x: T & {}) { +>f7 : (x: T & {}) => boolean +>x : T & {} + + return x instanceof Object && 'a' in x; +>x instanceof Object && 'a' in x : boolean +>x instanceof Object : boolean +>x : T & {} +>Object : ObjectConstructor +>'a' in x : boolean +>'a' : "a" +>x : T & {} +} + diff --git a/tests/cases/compiler/inKeywordAndIntersection.ts b/tests/cases/compiler/inKeywordAndIntersection.ts new file mode 100644 index 0000000000000..dadf93a8649d0 --- /dev/null +++ b/tests/cases/compiler/inKeywordAndIntersection.ts @@ -0,0 +1,31 @@ +// @strict: true + +class A { a = 0 } +class B { b = 0 } + +function f10(obj: A & { x: string } | B) { + if (obj instanceof Object) { + obj; // A & { x: string } | B + } + else { + obj; // Error + } +} + +// Repro from #50844 + +interface InstanceOne { + one(): void +} + +interface InstanceTwo { + two(): void +} + +const instance = {} as InstanceOne | InstanceTwo + +const ClassOne = {} as { new(): InstanceOne } & { foo: true }; + +if (instance instanceof ClassOne) { + instance.one(); +} diff --git a/tests/cases/compiler/inKeywordAndUnknown.ts b/tests/cases/compiler/inKeywordAndUnknown.ts index 745dfbc1f9af5..4a43fa3932bbe 100644 --- a/tests/cases/compiler/inKeywordAndUnknown.ts +++ b/tests/cases/compiler/inKeywordAndUnknown.ts @@ -16,3 +16,37 @@ function f(x: {}, y: unknown) { } y; // {} } + +// Repro from #51007 + +function isHTMLTable(table: unknown): boolean { + return !!table && table instanceof Object && 'html' in table; +} + +function f1(x: unknown) { + return x && x instanceof Object && 'a' in x; +} + +function f2(x: T) { + return x && x instanceof Object && 'a' in x; +} + +function f3(x: {}) { + return x instanceof Object && 'a' in x; +} + +function f4(x: T) { + return x instanceof Object && 'a' in x; +} + +function f5(x: T & {}) { + return x instanceof Object && 'a' in x; +} + +function f6(x: T & {}) { + return x instanceof Object && 'a' in x; +} + +function f7(x: T & {}) { + return x instanceof Object && 'a' in x; +}