Skip to content

Commit 6691c56

Browse files
committed
Narrow intersected classes using instanceof
1 parent 96894db commit 6691c56

File tree

4 files changed

+92
-5
lines changed

4 files changed

+92
-5
lines changed

src/compiler/checker.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17775,7 +17775,7 @@ namespace ts {
1777517775
target.flags & TypeFlags.Union ? some((target as UnionType).types, t => isTypeDerivedFrom(source, t)) :
1777617776
source.flags & TypeFlags.InstantiableNonPrimitive ? isTypeDerivedFrom(getBaseConstraintOfType(source) || unknownType, target) :
1777717777
target === globalObjectType ? !!(source.flags & (TypeFlags.Object | TypeFlags.NonPrimitive)) :
17778-
target === globalFunctionType ? !!(source.flags & TypeFlags.Object) && isFunctionObjectType(source as ObjectType) :
17778+
target === globalFunctionType ? isFunctionObjectType(source) :
1777917779
hasBaseType(source, getTargetType(target)) || (isArrayType(target) && !isReadonlyArrayType(target) && isTypeDerivedFrom(source, globalReadonlyArrayType));
1778017780
}
1778117781

@@ -23945,10 +23945,13 @@ namespace ts {
2394523945
return isTypeAssignableTo(assignedType, reducedType) ? reducedType : declaredType;
2394623946
}
2394723947

23948-
function isFunctionObjectType(type: ObjectType): boolean {
23948+
function isFunctionObjectType(type: Type): boolean {
23949+
if (!(type.flags & TypeFlags.StructuredType)) {
23950+
return false;
23951+
}
2394923952
// We do a quick check for a "bind" property before performing the more expensive subtype
2395023953
// check. This gives us a quicker out in the common case where an object type is not a function.
23951-
const resolved = resolveStructuredTypeMembers(type);
23954+
const resolved = resolveStructuredTypeMembers(type as StructuredType);
2395223955
return !!(resolved.callSignatures.length || resolved.constructSignatures.length ||
2395323956
resolved.members.get("bind" as __String) && isTypeSubtypeOf(type, globalFunctionType));
2395423957
}
@@ -23996,7 +23999,7 @@ namespace ts {
2399623999
if (flags & TypeFlags.Object) {
2399724000
return getObjectFlags(type) & ObjectFlags.Anonymous && isEmptyObjectType(type as ObjectType) ?
2399824001
strictNullChecks ? TypeFacts.EmptyObjectStrictFacts : TypeFacts.EmptyObjectFacts :
23999-
isFunctionObjectType(type as ObjectType) ?
24002+
isFunctionObjectType(type) ?
2400024003
strictNullChecks ? TypeFacts.FunctionStrictFacts : TypeFacts.FunctionFacts :
2400124004
strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts;
2400224005
}
@@ -34629,7 +34632,7 @@ namespace ts {
3462934632
declKind !== AssignmentDeclarationKind.ModuleExports &&
3463034633
declKind !== AssignmentDeclarationKind.Prototype &&
3463134634
!isEmptyObjectType(rightType) &&
34632-
!isFunctionObjectType(rightType as ObjectType) &&
34635+
!isFunctionObjectType(rightType) &&
3463334636
!(getObjectFlags(rightType) & ObjectFlags.Class)) {
3463434637
// don't check assignability of module.exports=, C.prototype=, or expando types because they will necessarily be incomplete
3463534638
checkAssignmentOperator(rightType);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
=== tests/cases/compiler/instanceofNarrowIntersection.ts ===
2+
// repro #50844
3+
4+
interface InstanceOne {
5+
>InstanceOne : Symbol(InstanceOne, Decl(instanceofNarrowIntersection.ts, 0, 0))
6+
7+
one(): void
8+
>one : Symbol(InstanceOne.one, Decl(instanceofNarrowIntersection.ts, 2, 23))
9+
}
10+
interface InstanceTwo {
11+
>InstanceTwo : Symbol(InstanceTwo, Decl(instanceofNarrowIntersection.ts, 4, 1))
12+
13+
two(): void
14+
>two : Symbol(InstanceTwo.two, Decl(instanceofNarrowIntersection.ts, 5, 23))
15+
}
16+
17+
declare const instance: InstanceOne | InstanceTwo
18+
>instance : Symbol(instance, Decl(instanceofNarrowIntersection.ts, 9, 13))
19+
>InstanceOne : Symbol(InstanceOne, Decl(instanceofNarrowIntersection.ts, 0, 0))
20+
>InstanceTwo : Symbol(InstanceTwo, Decl(instanceofNarrowIntersection.ts, 4, 1))
21+
22+
declare const SomeCls: { new (): InstanceOne } & { foo: true }
23+
>SomeCls : Symbol(SomeCls, Decl(instanceofNarrowIntersection.ts, 10, 13))
24+
>InstanceOne : Symbol(InstanceOne, Decl(instanceofNarrowIntersection.ts, 0, 0))
25+
>foo : Symbol(foo, Decl(instanceofNarrowIntersection.ts, 10, 50))
26+
27+
if (instance instanceof SomeCls) {
28+
>instance : Symbol(instance, Decl(instanceofNarrowIntersection.ts, 9, 13))
29+
>SomeCls : Symbol(SomeCls, Decl(instanceofNarrowIntersection.ts, 10, 13))
30+
31+
instance.one()
32+
>instance.one : Symbol(InstanceOne.one, Decl(instanceofNarrowIntersection.ts, 2, 23))
33+
>instance : Symbol(instance, Decl(instanceofNarrowIntersection.ts, 9, 13))
34+
>one : Symbol(InstanceOne.one, Decl(instanceofNarrowIntersection.ts, 2, 23))
35+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
=== tests/cases/compiler/instanceofNarrowIntersection.ts ===
2+
// repro #50844
3+
4+
interface InstanceOne {
5+
one(): void
6+
>one : () => void
7+
}
8+
interface InstanceTwo {
9+
two(): void
10+
>two : () => void
11+
}
12+
13+
declare const instance: InstanceOne | InstanceTwo
14+
>instance : InstanceOne | InstanceTwo
15+
16+
declare const SomeCls: { new (): InstanceOne } & { foo: true }
17+
>SomeCls : (new () => InstanceOne) & { foo: true; }
18+
>foo : true
19+
>true : true
20+
21+
if (instance instanceof SomeCls) {
22+
>instance instanceof SomeCls : boolean
23+
>instance : InstanceOne | InstanceTwo
24+
>SomeCls : (new () => InstanceOne) & { foo: true; }
25+
26+
instance.one()
27+
>instance.one() : void
28+
>instance.one : () => void
29+
>instance : InstanceOne
30+
>one : () => void
31+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
// repro #50844
5+
6+
interface InstanceOne {
7+
one(): void
8+
}
9+
interface InstanceTwo {
10+
two(): void
11+
}
12+
13+
declare const instance: InstanceOne | InstanceTwo
14+
declare const SomeCls: { new (): InstanceOne } & { foo: true }
15+
16+
if (instance instanceof SomeCls) {
17+
instance.one()
18+
}

0 commit comments

Comments
 (0)