diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 36f9bbb654b..c864a8aa76a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5365,25 +5365,25 @@ module ts { } } // Target type is type of constructor signiture - let constructorSignitures: Signature[]; + let constructSignitures: Signature[]; if (rightType.flags & TypeFlags.Interface) { - constructorSignitures = (rightType).declaredConstructSignatures; + constructSignitures = (rightType).declaredConstructSignatures; } if (rightType.flags & TypeFlags.Anonymous) { - constructorSignitures = (rightType).constructSignatures; + constructSignitures = (rightType).constructSignatures; } - if (constructorSignitures) { - let constructorType = getUnionType(map(constructorSignitures, constructorSignature => { - if (constructorSignature.typeParameters && constructorSignature.typeParameters.length !== 0) { - // TODO convert type parameters to any or empty object types. e.g. Sample -> Sample or Sample<{}>. + if (constructSignitures) { + let instanceType = getUnionType(map(constructSignitures, constructSignature => { + if (constructSignature.typeParameters && constructSignature.typeParameters.length !== 0) { + constructSignature = instantiateSignature(constructSignature, createTypeMapper(constructSignature.typeParameters, map(constructSignature.typeParameters, _ => anyType)), true) } - return constructorSignature.resolvedReturnType; + return constructSignature.resolvedReturnType; })); // Pickup type from union types if (type.flags & TypeFlags.Union) { - return getUnionType(filter((type).types, t => isTypeSubtypeOf(t, constructorType))); + return getUnionType(filter((type).types, t => isTypeSubtypeOf(t, instanceType))); } - return constructorType; + return instanceType; } return type; } diff --git a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt index c7146541705..1816ef77fc2 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt +++ b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt @@ -1,12 +1,16 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(12,10): error TS2339: Property 'bar' does not exist on type 'A'. -tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(32,10): error TS2339: Property 'foo' does not exist on type '{}'. -tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(33,10): error TS2339: Property 'foo' does not exist on type '{}'. -tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(34,10): error TS2339: Property 'bar' does not exist on type '{}'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(33,5): error TS2322: Type 'string' is not assignable to type 'number'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(34,10): error TS2339: Property 'bar' does not exist on type 'B'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(63,10): error TS2339: Property 'bar2' does not exist on type 'C1'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(82,10): error TS2339: Property 'bar' does not exist on type 'D'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(109,10): error TS2339: Property 'bar2' does not exist on type 'E1'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(131,11): error TS2339: Property 'foo' does not exist on type 'string | F'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(132,11): error TS2339: Property 'bar' does not exist on type 'string | F'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(157,11): error TS2339: Property 'foo2' does not exist on type 'G1'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(179,11): error TS2339: Property 'bar' does not exist on type 'H'. -==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts (6 errors) ==== +==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts (10 errors) ==== interface AConstructor { new (): A; } @@ -29,7 +33,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru obj2.bar; } - // with generics + // a construct signature with generics interface BConstructor { new (): B; } @@ -38,17 +42,15 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru } declare var B: BConstructor; - var obj3: B | A; - if (obj3 instanceof B) { // narrowed to B. - obj3.foo = "str"; - ~~~ -!!! error TS2339: Property 'foo' does not exist on type '{}'. + var obj3: B | string; + if (obj3 instanceof B) { // narrowed to B. obj3.foo = 1; - ~~~ -!!! error TS2339: Property 'foo' does not exist on type '{}'. + obj3.foo = "str"; + ~~~~~~~~ +!!! error TS2322: Type 'string' is not assignable to type 'number'. obj3.bar = "str"; ~~~ -!!! error TS2339: Property 'bar' does not exist on type '{}'. +!!! error TS2339: Property 'bar' does not exist on type 'B'. } var obj4: any; @@ -58,7 +60,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru obj4.bar = "str"; } - // has multiple constructor signature + // has multiple construct signature interface CConstructor { new (value: string): C1; new (value: number): C2; @@ -108,4 +110,111 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru obj8.foo; obj8.bar; } + + // a construct signature that returns a union type + interface EConstructor { + new (): E1 | E2; + } + interface E1 { + foo: string; + bar1: number; + } + interface E2 { + foo: string; + bar2: number; + } + declare var E: EConstructor; + + var obj9: E1 | A; + if (obj9 instanceof E) { // narrowed to E1. + obj9.foo; + obj9.bar1; + obj9.bar2; + ~~~~ +!!! error TS2339: Property 'bar2' does not exist on type 'E1'. + } + + var obj10: any; + if (obj10 instanceof E) { // can't type narrowing from any. + obj10.foo; + obj10.bar1; + obj10.bar2; + } + + // a construct signature that returns any + interface FConstructor { + new (): any; + } + interface F { + foo: string; + bar: number; + } + declare var F: FConstructor; + + var obj11: F | string; + if (obj11 instanceof F) { // can't type narrowing, construct signiture returns any. + obj11.foo; + ~~~ +!!! error TS2339: Property 'foo' does not exist on type 'string | F'. + obj11.bar; + ~~~ +!!! error TS2339: Property 'bar' does not exist on type 'string | F'. + } + + var obj12: any; + if (obj12 instanceof F) { // can't type narrowing from any. + obj12.foo; + obj12.bar; + } + + // a type with a prototype, it overrides the construct signiture + interface GConstructor { + prototype: G1; // high priority + new (): G2; // low priority + } + interface G1 { + foo1: number; + } + interface G2 { + foo2: boolean; + } + declare var G: GConstructor; + + var obj13: G1 | G2; + if (obj13 instanceof G) { // narrowed to G1. G1 is return type of prototype property. + obj13.foo1; + obj13.foo2; + ~~~~ +!!! error TS2339: Property 'foo2' does not exist on type 'G1'. + } + + var obj14: any; + if (obj14 instanceof G) { // can't type narrowing from any. + obj14.foo1; + obj14.foo2; + } + + // a type with a prototype that has any type + interface HConstructor { + prototype: any; // high priority, but any type is ignored. interface has implicit `prototype: any`. + new (): H; // low priority + } + interface H { + foo: number; + } + declare var H: HConstructor; + + var obj15: H | string; + if (obj15 instanceof H) { // narrowed to H. + obj15.foo; + obj15.bar; + ~~~ +!!! error TS2339: Property 'bar' does not exist on type 'H'. + } + + var obj16: any; + if (obj16 instanceof H) { // can't type narrowing from any. + obj16.foo1; + obj16.foo2; + } \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.js b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.js index c795126b0ff..395a5e96e05 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.js +++ b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.js @@ -19,7 +19,7 @@ if (obj2 instanceof A) { // can't type narrowing from any. obj2.bar; } -// with generics +// a construct signature with generics interface BConstructor { new (): B; } @@ -28,10 +28,10 @@ interface B { } declare var B: BConstructor; -var obj3: B | A; -if (obj3 instanceof B) { // narrowed to B. - obj3.foo = "str"; +var obj3: B | string; +if (obj3 instanceof B) { // narrowed to B. obj3.foo = 1; + obj3.foo = "str"; obj3.bar = "str"; } @@ -42,7 +42,7 @@ if (obj4 instanceof B) { // can't type narrowing from any. obj4.bar = "str"; } -// has multiple constructor signature +// has multiple construct signature interface CConstructor { new (value: string): C1; new (value: number): C2; @@ -89,6 +89,103 @@ if (obj8 instanceof D) { // can't type narrowing from any. obj8.bar; } +// a construct signature that returns a union type +interface EConstructor { + new (): E1 | E2; +} +interface E1 { + foo: string; + bar1: number; +} +interface E2 { + foo: string; + bar2: number; +} +declare var E: EConstructor; + +var obj9: E1 | A; +if (obj9 instanceof E) { // narrowed to E1. + obj9.foo; + obj9.bar1; + obj9.bar2; +} + +var obj10: any; +if (obj10 instanceof E) { // can't type narrowing from any. + obj10.foo; + obj10.bar1; + obj10.bar2; +} + +// a construct signature that returns any +interface FConstructor { + new (): any; +} +interface F { + foo: string; + bar: number; +} +declare var F: FConstructor; + +var obj11: F | string; +if (obj11 instanceof F) { // can't type narrowing, construct signiture returns any. + obj11.foo; + obj11.bar; +} + +var obj12: any; +if (obj12 instanceof F) { // can't type narrowing from any. + obj12.foo; + obj12.bar; +} + +// a type with a prototype, it overrides the construct signiture +interface GConstructor { + prototype: G1; // high priority + new (): G2; // low priority +} +interface G1 { + foo1: number; +} +interface G2 { + foo2: boolean; +} +declare var G: GConstructor; + +var obj13: G1 | G2; +if (obj13 instanceof G) { // narrowed to G1. G1 is return type of prototype property. + obj13.foo1; + obj13.foo2; +} + +var obj14: any; +if (obj14 instanceof G) { // can't type narrowing from any. + obj14.foo1; + obj14.foo2; +} + +// a type with a prototype that has any type +interface HConstructor { + prototype: any; // high priority, but any type is ignored. interface has implicit `prototype: any`. + new (): H; // low priority +} +interface H { + foo: number; +} +declare var H: HConstructor; + +var obj15: H | string; +if (obj15 instanceof H) { // narrowed to H. + obj15.foo; + obj15.bar; +} + +var obj16: any; +if (obj16 instanceof H) { // can't type narrowing from any. + obj16.foo1; + obj16.foo2; +} + //// [typeGuardsWithInstanceOfByConstructorSignature.js] var obj1; @@ -103,8 +200,8 @@ if (obj2 instanceof A) { } var obj3; if (obj3 instanceof B) { - obj3.foo = "str"; obj3.foo = 1; + obj3.foo = "str"; obj3.bar = "str"; } var obj4; @@ -135,3 +232,45 @@ if (obj8 instanceof D) { obj8.foo; obj8.bar; } +var obj9; +if (obj9 instanceof E) { + obj9.foo; + obj9.bar1; + obj9.bar2; +} +var obj10; +if (obj10 instanceof E) { + obj10.foo; + obj10.bar1; + obj10.bar2; +} +var obj11; +if (obj11 instanceof F) { + obj11.foo; + obj11.bar; +} +var obj12; +if (obj12 instanceof F) { + obj12.foo; + obj12.bar; +} +var obj13; +if (obj13 instanceof G) { + obj13.foo1; + obj13.foo2; +} +var obj14; +if (obj14 instanceof G) { + obj14.foo1; + obj14.foo2; +} +var obj15; +if (obj15 instanceof H) { + obj15.foo; + obj15.bar; +} +var obj16; +if (obj16 instanceof H) { + obj16.foo1; + obj16.foo2; +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts index b28d92e2c8e..c432ceb2bda 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts @@ -18,7 +18,7 @@ if (obj2 instanceof A) { // can't type narrowing from any. obj2.bar; } -// with generics +// a construct signature with generics interface BConstructor { new (): B; } @@ -27,10 +27,10 @@ interface B { } declare var B: BConstructor; -var obj3: B | A; -if (obj3 instanceof B) { // narrowed to B. - obj3.foo = "str"; +var obj3: B | string; +if (obj3 instanceof B) { // narrowed to B. obj3.foo = 1; + obj3.foo = "str"; obj3.bar = "str"; } @@ -41,7 +41,7 @@ if (obj4 instanceof B) { // can't type narrowing from any. obj4.bar = "str"; } -// has multiple constructor signature +// has multiple construct signature interface CConstructor { new (value: string): C1; new (value: number): C2; @@ -87,3 +87,100 @@ if (obj8 instanceof D) { // can't type narrowing from any. obj8.foo; obj8.bar; } + +// a construct signature that returns a union type +interface EConstructor { + new (): E1 | E2; +} +interface E1 { + foo: string; + bar1: number; +} +interface E2 { + foo: string; + bar2: number; +} +declare var E: EConstructor; + +var obj9: E1 | A; +if (obj9 instanceof E) { // narrowed to E1. + obj9.foo; + obj9.bar1; + obj9.bar2; +} + +var obj10: any; +if (obj10 instanceof E) { // can't type narrowing from any. + obj10.foo; + obj10.bar1; + obj10.bar2; +} + +// a construct signature that returns any +interface FConstructor { + new (): any; +} +interface F { + foo: string; + bar: number; +} +declare var F: FConstructor; + +var obj11: F | string; +if (obj11 instanceof F) { // can't type narrowing, construct signiture returns any. + obj11.foo; + obj11.bar; +} + +var obj12: any; +if (obj12 instanceof F) { // can't type narrowing from any. + obj12.foo; + obj12.bar; +} + +// a type with a prototype, it overrides the construct signiture +interface GConstructor { + prototype: G1; // high priority + new (): G2; // low priority +} +interface G1 { + foo1: number; +} +interface G2 { + foo2: boolean; +} +declare var G: GConstructor; + +var obj13: G1 | G2; +if (obj13 instanceof G) { // narrowed to G1. G1 is return type of prototype property. + obj13.foo1; + obj13.foo2; +} + +var obj14: any; +if (obj14 instanceof G) { // can't type narrowing from any. + obj14.foo1; + obj14.foo2; +} + +// a type with a prototype that has any type +interface HConstructor { + prototype: any; // high priority, but any type is ignored. interface has implicit `prototype: any`. + new (): H; // low priority +} +interface H { + foo: number; +} +declare var H: HConstructor; + +var obj15: H | string; +if (obj15 instanceof H) { // narrowed to H. + obj15.foo; + obj15.bar; +} + +var obj16: any; +if (obj16 instanceof H) { // can't type narrowing from any. + obj16.foo1; + obj16.foo2; +}