mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 16:38:05 -06:00
Don't narrow against unions of constructor functions with instanceof (#38099)
* WIP * Only narrows when the union is not all constructors
This commit is contained in:
parent
9a65658b28
commit
6ebd73c84f
@ -21800,6 +21800,12 @@ namespace ts {
|
||||
emptyObjectType;
|
||||
}
|
||||
|
||||
// We can't narrow a union based off instanceof without negated types see #31576 for more info
|
||||
if (!assumeTrue && rightType.flags & TypeFlags.Union) {
|
||||
const nonConstructorTypeInUnion = find((<UnionType>rightType).types, (t) => !isConstructorType(t));
|
||||
if (!nonConstructorTypeInUnion) return type;
|
||||
}
|
||||
|
||||
return getNarrowedType(type, targetType, assumeTrue, isTypeDerivedFrom);
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
//// [doesNotNarrowUnionOfConstructorsWithInstanceof.ts]
|
||||
class A {
|
||||
length: 1
|
||||
constructor() {
|
||||
this.length = 1
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
length: 2
|
||||
constructor() {
|
||||
this.length = 2
|
||||
}
|
||||
}
|
||||
|
||||
function getTypedArray(flag: boolean) {
|
||||
return flag ? new A() : new B();
|
||||
}
|
||||
function getTypedArrayConstructor(flag: boolean) {
|
||||
return flag ? A : B;
|
||||
}
|
||||
const a = getTypedArray(true); // A | B
|
||||
const b = getTypedArrayConstructor(false); // A constructor | B constructor
|
||||
|
||||
if (!(a instanceof b)) {
|
||||
console.log(a.length); // Used to be property 'length' does not exist on type 'never'.
|
||||
}
|
||||
|
||||
|
||||
//// [doesNotNarrowUnionOfConstructorsWithInstanceof.js]
|
||||
var A = /** @class */ (function () {
|
||||
function A() {
|
||||
this.length = 1;
|
||||
}
|
||||
return A;
|
||||
}());
|
||||
var B = /** @class */ (function () {
|
||||
function B() {
|
||||
this.length = 2;
|
||||
}
|
||||
return B;
|
||||
}());
|
||||
function getTypedArray(flag) {
|
||||
return flag ? new A() : new B();
|
||||
}
|
||||
function getTypedArrayConstructor(flag) {
|
||||
return flag ? A : B;
|
||||
}
|
||||
var a = getTypedArray(true); // A | B
|
||||
var b = getTypedArrayConstructor(false); // A constructor | B constructor
|
||||
if (!(a instanceof b)) {
|
||||
console.log(a.length); // Used to be property 'length' does not exist on type 'never'.
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
=== tests/cases/compiler/doesNotNarrowUnionOfConstructorsWithInstanceof.ts ===
|
||||
class A {
|
||||
>A : Symbol(A, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 0, 0))
|
||||
|
||||
length: 1
|
||||
>length : Symbol(A.length, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 0, 9))
|
||||
|
||||
constructor() {
|
||||
this.length = 1
|
||||
>this.length : Symbol(A.length, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 0, 9))
|
||||
>this : Symbol(A, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 0, 0))
|
||||
>length : Symbol(A.length, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 0, 9))
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
>B : Symbol(B, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 5, 1))
|
||||
|
||||
length: 2
|
||||
>length : Symbol(B.length, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 7, 9))
|
||||
|
||||
constructor() {
|
||||
this.length = 2
|
||||
>this.length : Symbol(B.length, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 7, 9))
|
||||
>this : Symbol(B, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 5, 1))
|
||||
>length : Symbol(B.length, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 7, 9))
|
||||
}
|
||||
}
|
||||
|
||||
function getTypedArray(flag: boolean) {
|
||||
>getTypedArray : Symbol(getTypedArray, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 12, 1))
|
||||
>flag : Symbol(flag, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 14, 23))
|
||||
|
||||
return flag ? new A() : new B();
|
||||
>flag : Symbol(flag, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 14, 23))
|
||||
>A : Symbol(A, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 0, 0))
|
||||
>B : Symbol(B, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 5, 1))
|
||||
}
|
||||
function getTypedArrayConstructor(flag: boolean) {
|
||||
>getTypedArrayConstructor : Symbol(getTypedArrayConstructor, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 16, 1))
|
||||
>flag : Symbol(flag, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 17, 34))
|
||||
|
||||
return flag ? A : B;
|
||||
>flag : Symbol(flag, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 17, 34))
|
||||
>A : Symbol(A, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 0, 0))
|
||||
>B : Symbol(B, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 5, 1))
|
||||
}
|
||||
const a = getTypedArray(true); // A | B
|
||||
>a : Symbol(a, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 20, 5))
|
||||
>getTypedArray : Symbol(getTypedArray, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 12, 1))
|
||||
|
||||
const b = getTypedArrayConstructor(false); // A constructor | B constructor
|
||||
>b : Symbol(b, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 21, 5))
|
||||
>getTypedArrayConstructor : Symbol(getTypedArrayConstructor, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 16, 1))
|
||||
|
||||
if (!(a instanceof b)) {
|
||||
>a : Symbol(a, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 20, 5))
|
||||
>b : Symbol(b, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 21, 5))
|
||||
|
||||
console.log(a.length); // Used to be property 'length' does not exist on type 'never'.
|
||||
>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, --, --))
|
||||
>a.length : Symbol(length, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 0, 9), Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 7, 9))
|
||||
>a : Symbol(a, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 20, 5))
|
||||
>length : Symbol(length, Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 0, 9), Decl(doesNotNarrowUnionOfConstructorsWithInstanceof.ts, 7, 9))
|
||||
}
|
||||
|
||||
@ -0,0 +1,84 @@
|
||||
=== tests/cases/compiler/doesNotNarrowUnionOfConstructorsWithInstanceof.ts ===
|
||||
class A {
|
||||
>A : A
|
||||
|
||||
length: 1
|
||||
>length : 1
|
||||
|
||||
constructor() {
|
||||
this.length = 1
|
||||
>this.length = 1 : 1
|
||||
>this.length : 1
|
||||
>this : this
|
||||
>length : 1
|
||||
>1 : 1
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
>B : B
|
||||
|
||||
length: 2
|
||||
>length : 2
|
||||
|
||||
constructor() {
|
||||
this.length = 2
|
||||
>this.length = 2 : 2
|
||||
>this.length : 2
|
||||
>this : this
|
||||
>length : 2
|
||||
>2 : 2
|
||||
}
|
||||
}
|
||||
|
||||
function getTypedArray(flag: boolean) {
|
||||
>getTypedArray : (flag: boolean) => A | B
|
||||
>flag : boolean
|
||||
|
||||
return flag ? new A() : new B();
|
||||
>flag ? new A() : new B() : A | B
|
||||
>flag : boolean
|
||||
>new A() : A
|
||||
>A : typeof A
|
||||
>new B() : B
|
||||
>B : typeof B
|
||||
}
|
||||
function getTypedArrayConstructor(flag: boolean) {
|
||||
>getTypedArrayConstructor : (flag: boolean) => typeof A | typeof B
|
||||
>flag : boolean
|
||||
|
||||
return flag ? A : B;
|
||||
>flag ? A : B : typeof A | typeof B
|
||||
>flag : boolean
|
||||
>A : typeof A
|
||||
>B : typeof B
|
||||
}
|
||||
const a = getTypedArray(true); // A | B
|
||||
>a : A | B
|
||||
>getTypedArray(true) : A | B
|
||||
>getTypedArray : (flag: boolean) => A | B
|
||||
>true : true
|
||||
|
||||
const b = getTypedArrayConstructor(false); // A constructor | B constructor
|
||||
>b : typeof A | typeof B
|
||||
>getTypedArrayConstructor(false) : typeof A | typeof B
|
||||
>getTypedArrayConstructor : (flag: boolean) => typeof A | typeof B
|
||||
>false : false
|
||||
|
||||
if (!(a instanceof b)) {
|
||||
>!(a instanceof b) : boolean
|
||||
>(a instanceof b) : boolean
|
||||
>a instanceof b : boolean
|
||||
>a : A | B
|
||||
>b : typeof A | typeof B
|
||||
|
||||
console.log(a.length); // Used to be property 'length' does not exist on type 'never'.
|
||||
>console.log(a.length) : void
|
||||
>console.log : (...data: any[]) => void
|
||||
>console : Console
|
||||
>log : (...data: any[]) => void
|
||||
>a.length : 1 | 2
|
||||
>a : A | B
|
||||
>length : 1 | 2
|
||||
}
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
class A {
|
||||
length: 1
|
||||
constructor() {
|
||||
this.length = 1
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
length: 2
|
||||
constructor() {
|
||||
this.length = 2
|
||||
}
|
||||
}
|
||||
|
||||
function getTypedArray(flag: boolean) {
|
||||
return flag ? new A() : new B();
|
||||
}
|
||||
function getTypedArrayConstructor(flag: boolean) {
|
||||
return flag ? A : B;
|
||||
}
|
||||
const a = getTypedArray(true); // A | B
|
||||
const b = getTypedArrayConstructor(false); // A constructor | B constructor
|
||||
|
||||
if (!(a instanceof b)) {
|
||||
console.log(a.length); // Used to be property 'length' does not exist on type 'never'.
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user