Skip to content

Commit f074049

Browse files
authored
Replace subtype check in derivedness check with flags and structure checks (microsoft#27403)
* Replace subtype check in derivedness check with flags and structure checks * Remove now extraneous clause
1 parent 62306bc commit f074049

File tree

5 files changed

+259
-3
lines changed

5 files changed

+259
-3
lines changed

src/compiler/checker.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10616,9 +10616,9 @@ namespace ts {
1061610616
function isTypeDerivedFrom(source: Type, target: Type): boolean {
1061710617
return source.flags & TypeFlags.Union ? every((<UnionType>source).types, t => isTypeDerivedFrom(t, target)) :
1061810618
target.flags & TypeFlags.Union ? some((<UnionType>target).types, t => isTypeDerivedFrom(source, t)) :
10619-
source.flags & TypeFlags.Primitive && !(target.flags & TypeFlags.Primitive) ? false :
1062010619
source.flags & TypeFlags.InstantiableNonPrimitive ? isTypeDerivedFrom(getBaseConstraintOfType(source) || emptyObjectType, target) :
10621-
target === globalObjectType || target === globalFunctionType ? isTypeSubtypeOf(source, target) :
10620+
target === globalObjectType ? !!(source.flags & (TypeFlags.Object | TypeFlags.NonPrimitive)) :
10621+
target === globalFunctionType ? isFunctionObjectType(source as ObjectType) :
1062210622
hasBaseType(source, getTargetType(target));
1062310623
}
1062410624

@@ -15371,7 +15371,7 @@ namespace ts {
1537115371

1537215372
// Check that right operand is a function type with a prototype property
1537315373
const rightType = getTypeOfExpression(expr.right);
15374-
if (!isTypeSubtypeOf(rightType, globalFunctionType)) {
15374+
if (!isTypeDerivedFrom(rightType, globalFunctionType)) {
1537515375
return type;
1537615376
}
1537715377

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//// [controlFlowInstanceofExtendsFunction.ts]
2+
declare global {
3+
interface Function {
4+
now(): string;
5+
}
6+
}
7+
8+
Function.prototype.now = function () {
9+
return "now"
10+
}
11+
12+
class X {
13+
static now() {
14+
return {}
15+
}
16+
17+
why() {
18+
19+
}
20+
}
21+
22+
class Y {
23+
24+
}
25+
26+
console.log(X.now()) // works as expected
27+
console.log(Y.now()) // works as expected
28+
29+
export const x: X | number = Math.random() > 0.5 ? new X() : 1
30+
31+
if (x instanceof X) {
32+
x.why() // should compile
33+
}
34+
35+
//// [controlFlowInstanceofExtendsFunction.js]
36+
"use strict";
37+
exports.__esModule = true;
38+
Function.prototype.now = function () {
39+
return "now";
40+
};
41+
var X = /** @class */ (function () {
42+
function X() {
43+
}
44+
X.now = function () {
45+
return {};
46+
};
47+
X.prototype.why = function () {
48+
};
49+
return X;
50+
}());
51+
var Y = /** @class */ (function () {
52+
function Y() {
53+
}
54+
return Y;
55+
}());
56+
console.log(X.now()); // works as expected
57+
console.log(Y.now()); // works as expected
58+
exports.x = Math.random() > 0.5 ? new X() : 1;
59+
if (exports.x instanceof X) {
60+
exports.x.why(); // should compile
61+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
=== tests/cases/conformance/controlFlow/controlFlowInstanceofExtendsFunction.ts ===
2+
declare global {
3+
>global : Symbol(global, Decl(controlFlowInstanceofExtendsFunction.ts, 0, 0))
4+
5+
interface Function {
6+
>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(controlFlowInstanceofExtendsFunction.ts, 0, 16))
7+
8+
now(): string;
9+
>now : Symbol(Function.now, Decl(controlFlowInstanceofExtendsFunction.ts, 1, 24))
10+
}
11+
}
12+
13+
Function.prototype.now = function () {
14+
>Function.prototype.now : Symbol(Function.now, Decl(controlFlowInstanceofExtendsFunction.ts, 1, 24))
15+
>Function.prototype : Symbol(FunctionConstructor.prototype, Decl(lib.es5.d.ts, --, --))
16+
>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(controlFlowInstanceofExtendsFunction.ts, 0, 16))
17+
>prototype : Symbol(FunctionConstructor.prototype, Decl(lib.es5.d.ts, --, --))
18+
>now : Symbol(Function.now, Decl(controlFlowInstanceofExtendsFunction.ts, 1, 24))
19+
20+
return "now"
21+
}
22+
23+
class X {
24+
>X : Symbol(X, Decl(controlFlowInstanceofExtendsFunction.ts, 8, 1))
25+
26+
static now() {
27+
>now : Symbol(X.now, Decl(controlFlowInstanceofExtendsFunction.ts, 10, 9))
28+
29+
return {}
30+
}
31+
32+
why() {
33+
>why : Symbol(X.why, Decl(controlFlowInstanceofExtendsFunction.ts, 13, 5))
34+
35+
}
36+
}
37+
38+
class Y {
39+
>Y : Symbol(Y, Decl(controlFlowInstanceofExtendsFunction.ts, 18, 1))
40+
41+
}
42+
43+
console.log(X.now()) // works as expected
44+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
45+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
46+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
47+
>X.now : Symbol(X.now, Decl(controlFlowInstanceofExtendsFunction.ts, 10, 9))
48+
>X : Symbol(X, Decl(controlFlowInstanceofExtendsFunction.ts, 8, 1))
49+
>now : Symbol(X.now, Decl(controlFlowInstanceofExtendsFunction.ts, 10, 9))
50+
51+
console.log(Y.now()) // works as expected
52+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
53+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
54+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
55+
>Y.now : Symbol(Function.now, Decl(controlFlowInstanceofExtendsFunction.ts, 1, 24))
56+
>Y : Symbol(Y, Decl(controlFlowInstanceofExtendsFunction.ts, 18, 1))
57+
>now : Symbol(Function.now, Decl(controlFlowInstanceofExtendsFunction.ts, 1, 24))
58+
59+
export const x: X | number = Math.random() > 0.5 ? new X() : 1
60+
>x : Symbol(x, Decl(controlFlowInstanceofExtendsFunction.ts, 27, 12))
61+
>X : Symbol(X, Decl(controlFlowInstanceofExtendsFunction.ts, 8, 1))
62+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
63+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
64+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
65+
>X : Symbol(X, Decl(controlFlowInstanceofExtendsFunction.ts, 8, 1))
66+
67+
if (x instanceof X) {
68+
>x : Symbol(x, Decl(controlFlowInstanceofExtendsFunction.ts, 27, 12))
69+
>X : Symbol(X, Decl(controlFlowInstanceofExtendsFunction.ts, 8, 1))
70+
71+
x.why() // should compile
72+
>x.why : Symbol(X.why, Decl(controlFlowInstanceofExtendsFunction.ts, 13, 5))
73+
>x : Symbol(x, Decl(controlFlowInstanceofExtendsFunction.ts, 27, 12))
74+
>why : Symbol(X.why, Decl(controlFlowInstanceofExtendsFunction.ts, 13, 5))
75+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
=== tests/cases/conformance/controlFlow/controlFlowInstanceofExtendsFunction.ts ===
2+
declare global {
3+
>global : any
4+
5+
interface Function {
6+
now(): string;
7+
>now : () => string
8+
}
9+
}
10+
11+
Function.prototype.now = function () {
12+
>Function.prototype.now = function () { return "now"} : () => string
13+
>Function.prototype.now : () => string
14+
>Function.prototype : Function
15+
>Function : FunctionConstructor
16+
>prototype : Function
17+
>now : () => string
18+
>function () { return "now"} : () => string
19+
20+
return "now"
21+
>"now" : "now"
22+
}
23+
24+
class X {
25+
>X : X
26+
27+
static now() {
28+
>now : () => {}
29+
30+
return {}
31+
>{} : {}
32+
}
33+
34+
why() {
35+
>why : () => void
36+
37+
}
38+
}
39+
40+
class Y {
41+
>Y : Y
42+
43+
}
44+
45+
console.log(X.now()) // works as expected
46+
>console.log(X.now()) : void
47+
>console.log : (message?: any, ...optionalParams: any[]) => void
48+
>console : Console
49+
>log : (message?: any, ...optionalParams: any[]) => void
50+
>X.now() : {}
51+
>X.now : () => {}
52+
>X : typeof X
53+
>now : () => {}
54+
55+
console.log(Y.now()) // works as expected
56+
>console.log(Y.now()) : void
57+
>console.log : (message?: any, ...optionalParams: any[]) => void
58+
>console : Console
59+
>log : (message?: any, ...optionalParams: any[]) => void
60+
>Y.now() : string
61+
>Y.now : () => string
62+
>Y : typeof Y
63+
>now : () => string
64+
65+
export const x: X | number = Math.random() > 0.5 ? new X() : 1
66+
>x : number | X
67+
>Math.random() > 0.5 ? new X() : 1 : X | 1
68+
>Math.random() > 0.5 : boolean
69+
>Math.random() : number
70+
>Math.random : () => number
71+
>Math : Math
72+
>random : () => number
73+
>0.5 : 0.5
74+
>new X() : X
75+
>X : typeof X
76+
>1 : 1
77+
78+
if (x instanceof X) {
79+
>x instanceof X : boolean
80+
>x : number | X
81+
>X : typeof X
82+
83+
x.why() // should compile
84+
>x.why() : void
85+
>x.why : () => void
86+
>x : X
87+
>why : () => void
88+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
declare global {
2+
interface Function {
3+
now(): string;
4+
}
5+
}
6+
7+
Function.prototype.now = function () {
8+
return "now"
9+
}
10+
11+
class X {
12+
static now() {
13+
return {}
14+
}
15+
16+
why() {
17+
18+
}
19+
}
20+
21+
class Y {
22+
23+
}
24+
25+
console.log(X.now()) // works as expected
26+
console.log(Y.now()) // works as expected
27+
28+
export const x: X | number = Math.random() > 0.5 ? new X() : 1
29+
30+
if (x instanceof X) {
31+
x.why() // should compile
32+
}

0 commit comments

Comments
 (0)