Replace subtype check in derivedness check with flags and structure checks (#27403)

* Replace subtype check in derivedness check with flags and structure checks

* Remove now extraneous clause
This commit is contained in:
Wesley Wigham
2018-10-04 12:55:39 -07:00
committed by GitHub
parent 62306bc3f9
commit f07404938f
5 changed files with 259 additions and 3 deletions
+3 -3
View File
@@ -10616,9 +10616,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));
}
@@ -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;
}
@@ -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
}
@@ -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))
}
@@ -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
}
@@ -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
}