diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 21518ca34d5..2f020f46f22 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10616,9 +10616,9 @@ namespace ts { 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)) : - source.flags & TypeFlags.Primitive && !(target.flags & TypeFlags.Primitive) ? false : source.flags & TypeFlags.InstantiableNonPrimitive ? isTypeDerivedFrom(getBaseConstraintOfType(source) || emptyObjectType, target) : - target === globalObjectType || target === globalFunctionType ? isTypeSubtypeOf(source, target) : + target === globalObjectType ? !!(source.flags & (TypeFlags.Object | TypeFlags.NonPrimitive)) : + target === globalFunctionType ? isFunctionObjectType(source as ObjectType) : hasBaseType(source, getTargetType(target)); } @@ -15371,7 +15371,7 @@ namespace ts { // Check that right operand is a function type with a prototype property const rightType = getTypeOfExpression(expr.right); - if (!isTypeSubtypeOf(rightType, globalFunctionType)) { + if (!isTypeDerivedFrom(rightType, globalFunctionType)) { return type; } diff --git a/tests/baselines/reference/controlFlowInstanceofExtendsFunction.js b/tests/baselines/reference/controlFlowInstanceofExtendsFunction.js new file mode 100644 index 00000000000..950aa25b576 --- /dev/null +++ b/tests/baselines/reference/controlFlowInstanceofExtendsFunction.js @@ -0,0 +1,61 @@ +//// [controlFlowInstanceofExtendsFunction.ts] +declare global { + interface Function { + now(): string; + } +} + +Function.prototype.now = function () { + return "now" +} + +class X { + static now() { + return {} + } + + why() { + + } +} + +class Y { + +} + +console.log(X.now()) // works as expected +console.log(Y.now()) // works as expected + +export const x: X | number = Math.random() > 0.5 ? new X() : 1 + +if (x instanceof X) { + x.why() // should compile +} + +//// [controlFlowInstanceofExtendsFunction.js] +"use strict"; +exports.__esModule = true; +Function.prototype.now = function () { + return "now"; +}; +var X = /** @class */ (function () { + function X() { + } + X.now = function () { + return {}; + }; + X.prototype.why = function () { + }; + return X; +}()); +var Y = /** @class */ (function () { + function Y() { + } + return Y; +}()); +console.log(X.now()); // works as expected +console.log(Y.now()); // works as expected +exports.x = Math.random() > 0.5 ? new X() : 1; +if (exports.x instanceof X) { + exports.x.why(); // should compile +} diff --git a/tests/baselines/reference/controlFlowInstanceofExtendsFunction.symbols b/tests/baselines/reference/controlFlowInstanceofExtendsFunction.symbols new file mode 100644 index 00000000000..c5ac2e71eda --- /dev/null +++ b/tests/baselines/reference/controlFlowInstanceofExtendsFunction.symbols @@ -0,0 +1,75 @@ +=== tests/cases/conformance/controlFlow/controlFlowInstanceofExtendsFunction.ts === +declare global { +>global : Symbol(global, Decl(controlFlowInstanceofExtendsFunction.ts, 0, 0)) + + interface Function { +>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(controlFlowInstanceofExtendsFunction.ts, 0, 16)) + + now(): string; +>now : Symbol(Function.now, Decl(controlFlowInstanceofExtendsFunction.ts, 1, 24)) + } +} + +Function.prototype.now = function () { +>Function.prototype.now : Symbol(Function.now, Decl(controlFlowInstanceofExtendsFunction.ts, 1, 24)) +>Function.prototype : Symbol(FunctionConstructor.prototype, Decl(lib.es5.d.ts, --, --)) +>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(controlFlowInstanceofExtendsFunction.ts, 0, 16)) +>prototype : Symbol(FunctionConstructor.prototype, Decl(lib.es5.d.ts, --, --)) +>now : Symbol(Function.now, Decl(controlFlowInstanceofExtendsFunction.ts, 1, 24)) + + return "now" +} + +class X { +>X : Symbol(X, Decl(controlFlowInstanceofExtendsFunction.ts, 8, 1)) + + static now() { +>now : Symbol(X.now, Decl(controlFlowInstanceofExtendsFunction.ts, 10, 9)) + + return {} + } + + why() { +>why : Symbol(X.why, Decl(controlFlowInstanceofExtendsFunction.ts, 13, 5)) + + } +} + +class Y { +>Y : Symbol(Y, Decl(controlFlowInstanceofExtendsFunction.ts, 18, 1)) + +} + +console.log(X.now()) // works as expected +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>X.now : Symbol(X.now, Decl(controlFlowInstanceofExtendsFunction.ts, 10, 9)) +>X : Symbol(X, Decl(controlFlowInstanceofExtendsFunction.ts, 8, 1)) +>now : Symbol(X.now, Decl(controlFlowInstanceofExtendsFunction.ts, 10, 9)) + +console.log(Y.now()) // works as expected +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>Y.now : Symbol(Function.now, Decl(controlFlowInstanceofExtendsFunction.ts, 1, 24)) +>Y : Symbol(Y, Decl(controlFlowInstanceofExtendsFunction.ts, 18, 1)) +>now : Symbol(Function.now, Decl(controlFlowInstanceofExtendsFunction.ts, 1, 24)) + +export const x: X | number = Math.random() > 0.5 ? new X() : 1 +>x : Symbol(x, Decl(controlFlowInstanceofExtendsFunction.ts, 27, 12)) +>X : Symbol(X, Decl(controlFlowInstanceofExtendsFunction.ts, 8, 1)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>X : Symbol(X, Decl(controlFlowInstanceofExtendsFunction.ts, 8, 1)) + +if (x instanceof X) { +>x : Symbol(x, Decl(controlFlowInstanceofExtendsFunction.ts, 27, 12)) +>X : Symbol(X, Decl(controlFlowInstanceofExtendsFunction.ts, 8, 1)) + + x.why() // should compile +>x.why : Symbol(X.why, Decl(controlFlowInstanceofExtendsFunction.ts, 13, 5)) +>x : Symbol(x, Decl(controlFlowInstanceofExtendsFunction.ts, 27, 12)) +>why : Symbol(X.why, Decl(controlFlowInstanceofExtendsFunction.ts, 13, 5)) +} diff --git a/tests/baselines/reference/controlFlowInstanceofExtendsFunction.types b/tests/baselines/reference/controlFlowInstanceofExtendsFunction.types new file mode 100644 index 00000000000..397b0e2e76a --- /dev/null +++ b/tests/baselines/reference/controlFlowInstanceofExtendsFunction.types @@ -0,0 +1,88 @@ +=== tests/cases/conformance/controlFlow/controlFlowInstanceofExtendsFunction.ts === +declare global { +>global : any + + interface Function { + now(): string; +>now : () => string + } +} + +Function.prototype.now = function () { +>Function.prototype.now = function () { return "now"} : () => string +>Function.prototype.now : () => string +>Function.prototype : Function +>Function : FunctionConstructor +>prototype : Function +>now : () => string +>function () { return "now"} : () => string + + return "now" +>"now" : "now" +} + +class X { +>X : X + + static now() { +>now : () => {} + + return {} +>{} : {} + } + + why() { +>why : () => void + + } +} + +class Y { +>Y : Y + +} + +console.log(X.now()) // works as expected +>console.log(X.now()) : void +>console.log : (message?: any, ...optionalParams: any[]) => void +>console : Console +>log : (message?: any, ...optionalParams: any[]) => void +>X.now() : {} +>X.now : () => {} +>X : typeof X +>now : () => {} + +console.log(Y.now()) // works as expected +>console.log(Y.now()) : void +>console.log : (message?: any, ...optionalParams: any[]) => void +>console : Console +>log : (message?: any, ...optionalParams: any[]) => void +>Y.now() : string +>Y.now : () => string +>Y : typeof Y +>now : () => string + +export const x: X | number = Math.random() > 0.5 ? new X() : 1 +>x : number | X +>Math.random() > 0.5 ? new X() : 1 : X | 1 +>Math.random() > 0.5 : boolean +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>0.5 : 0.5 +>new X() : X +>X : typeof X +>1 : 1 + +if (x instanceof X) { +>x instanceof X : boolean +>x : number | X +>X : typeof X + + x.why() // should compile +>x.why() : void +>x.why : () => void +>x : X +>why : () => void +} diff --git a/tests/cases/conformance/controlFlow/controlFlowInstanceofExtendsFunction.ts b/tests/cases/conformance/controlFlow/controlFlowInstanceofExtendsFunction.ts new file mode 100644 index 00000000000..4a1ff7ff867 --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowInstanceofExtendsFunction.ts @@ -0,0 +1,32 @@ +declare global { + interface Function { + now(): string; + } +} + +Function.prototype.now = function () { + return "now" +} + +class X { + static now() { + return {} + } + + why() { + + } +} + +class Y { + +} + +console.log(X.now()) // works as expected +console.log(Y.now()) // works as expected + +export const x: X | number = Math.random() > 0.5 ? new X() : 1 + +if (x instanceof X) { + x.why() // should compile +} \ No newline at end of file