Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 3 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10595,9 +10595,9 @@ namespace ts {
function isTypeDerivedFrom(source: Type, target: Type): boolean {
return source.flags & TypeFlags.Union ? every((<UnionType>source).types, t => isTypeDerivedFrom(t, target)) :
target.flags & TypeFlags.Union ? some((<UnionType>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));
}

Expand Down Expand Up @@ -15349,7 +15349,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;
}

Expand Down
61 changes: 61 additions & 0 deletions tests/baselines/reference/controlFlowInstanceofExtendsFunction.js
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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))
}
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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
}