type narrowing by constructor signiture of interface

This commit is contained in:
vvakame
2015-04-30 23:12:35 +09:00
parent 64f3798bd7
commit 35d6e98e98
4 changed files with 368 additions and 9 deletions
+31 -9
View File
@@ -5351,17 +5351,39 @@ module ts {
}
// Target type is type of prototype property
let prototypeProperty = getPropertyOfType(rightType, "prototype");
if (!prototypeProperty) {
return type;
if (prototypeProperty) {
let targetType = getTypeOfSymbol(prototypeProperty);
if (targetType !== anyType) {
// Narrow to target type if it is a subtype of current type
if (isTypeSubtypeOf(targetType, type)) {
return targetType;
}
// If current type is a union type, remove all constituents that aren't subtypes of target type
if (type.flags & TypeFlags.Union) {
return getUnionType(filter((<UnionType>type).types, t => isTypeSubtypeOf(t, targetType)));
}
}
}
let targetType = getTypeOfSymbol(prototypeProperty);
// Narrow to target type if it is a subtype of current type
if (isTypeSubtypeOf(targetType, type)) {
return targetType;
// Target type is type of constructor signiture
let constructorSignitures: Signature[];
if (rightType.flags & TypeFlags.Interface) {
constructorSignitures = (<InterfaceTypeWithDeclaredMembers>rightType).declaredConstructSignatures;
}
// If current type is a union type, remove all constituents that aren't subtypes of target type
if (type.flags & TypeFlags.Union) {
return getUnionType(filter((<UnionType>type).types, t => isTypeSubtypeOf(t, targetType)));
if (rightType.flags & TypeFlags.Anonymous) {
constructorSignitures = (<ResolvedType>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<T> -> Sample<any> or Sample<{}>.
}
return constructorSignature.resolvedReturnType;
}));
// Pickup type from union types
if (type.flags & TypeFlags.Union) {
return getUnionType(filter((<UnionType>type).types, t => isTypeSubtypeOf(t, constructorType)));
}
return constructorType;
}
return type;
}
@@ -0,0 +1,111 @@
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(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 (6 errors) ====
interface AConstructor {
new (): A;
}
interface A {
foo: string;
}
declare var A: AConstructor;
var obj1: A | string;
if (obj1 instanceof A) { // narrowed to A.
obj1.foo;
obj1.bar;
~~~
!!! error TS2339: Property 'bar' does not exist on type 'A'.
}
var obj2: any;
if (obj2 instanceof A) { // can't type narrowing from any.
obj2.foo;
obj2.bar;
}
// with generics
interface BConstructor {
new <T>(): B<T>;
}
interface B<T> {
foo: T;
}
declare var B: BConstructor;
var obj3: B<string> | A;
if (obj3 instanceof B) { // narrowed to B<string>.
obj3.foo = "str";
~~~
!!! error TS2339: Property 'foo' does not exist on type '{}'.
obj3.foo = 1;
~~~
!!! error TS2339: Property 'foo' does not exist on type '{}'.
obj3.bar = "str";
~~~
!!! error TS2339: Property 'bar' does not exist on type '{}'.
}
var obj4: any;
if (obj4 instanceof B) { // can't type narrowing from any.
obj4.foo = "str";
obj4.foo = 1;
obj4.bar = "str";
}
// has multiple constructor signature
interface CConstructor {
new (value: string): C1;
new (value: number): C2;
}
interface C1 {
foo: string;
bar1: number;
}
interface C2 {
foo: string;
bar2: number;
}
declare var C: CConstructor;
var obj5: C1 | A;
if (obj5 instanceof C) { // narrowed to C1.
obj5.foo;
obj5.bar1;
obj5.bar2;
~~~~
!!! error TS2339: Property 'bar2' does not exist on type 'C1'.
}
var obj6: any;
if (obj6 instanceof C) { // can't type narrowing from any.
obj6.foo;
obj6.bar1;
obj6.bar2;
}
// with object type literal
interface D {
foo: string;
}
declare var D: { new (): D; };
var obj7: D | string;
if (obj7 instanceof D) { // narrowed to D.
obj7.foo;
obj7.bar;
~~~
!!! error TS2339: Property 'bar' does not exist on type 'D'.
}
var obj8: any;
if (obj8 instanceof D) { // can't type narrowing from any.
obj8.foo;
obj8.bar;
}
@@ -0,0 +1,137 @@
//// [typeGuardsWithInstanceOfByConstructorSignature.ts]
interface AConstructor {
new (): A;
}
interface A {
foo: string;
}
declare var A: AConstructor;
var obj1: A | string;
if (obj1 instanceof A) { // narrowed to A.
obj1.foo;
obj1.bar;
}
var obj2: any;
if (obj2 instanceof A) { // can't type narrowing from any.
obj2.foo;
obj2.bar;
}
// with generics
interface BConstructor {
new <T>(): B<T>;
}
interface B<T> {
foo: T;
}
declare var B: BConstructor;
var obj3: B<string> | A;
if (obj3 instanceof B) { // narrowed to B<string>.
obj3.foo = "str";
obj3.foo = 1;
obj3.bar = "str";
}
var obj4: any;
if (obj4 instanceof B) { // can't type narrowing from any.
obj4.foo = "str";
obj4.foo = 1;
obj4.bar = "str";
}
// has multiple constructor signature
interface CConstructor {
new (value: string): C1;
new (value: number): C2;
}
interface C1 {
foo: string;
bar1: number;
}
interface C2 {
foo: string;
bar2: number;
}
declare var C: CConstructor;
var obj5: C1 | A;
if (obj5 instanceof C) { // narrowed to C1.
obj5.foo;
obj5.bar1;
obj5.bar2;
}
var obj6: any;
if (obj6 instanceof C) { // can't type narrowing from any.
obj6.foo;
obj6.bar1;
obj6.bar2;
}
// with object type literal
interface D {
foo: string;
}
declare var D: { new (): D; };
var obj7: D | string;
if (obj7 instanceof D) { // narrowed to D.
obj7.foo;
obj7.bar;
}
var obj8: any;
if (obj8 instanceof D) { // can't type narrowing from any.
obj8.foo;
obj8.bar;
}
//// [typeGuardsWithInstanceOfByConstructorSignature.js]
var obj1;
if (obj1 instanceof A) {
obj1.foo;
obj1.bar;
}
var obj2;
if (obj2 instanceof A) {
obj2.foo;
obj2.bar;
}
var obj3;
if (obj3 instanceof B) {
obj3.foo = "str";
obj3.foo = 1;
obj3.bar = "str";
}
var obj4;
if (obj4 instanceof B) {
obj4.foo = "str";
obj4.foo = 1;
obj4.bar = "str";
}
var obj5;
if (obj5 instanceof C) {
obj5.foo;
obj5.bar1;
obj5.bar2;
}
var obj6;
if (obj6 instanceof C) {
obj6.foo;
obj6.bar1;
obj6.bar2;
}
var obj7;
if (obj7 instanceof D) {
obj7.foo;
obj7.bar;
}
var obj8;
if (obj8 instanceof D) {
obj8.foo;
obj8.bar;
}
@@ -0,0 +1,89 @@
interface AConstructor {
new (): A;
}
interface A {
foo: string;
}
declare var A: AConstructor;
var obj1: A | string;
if (obj1 instanceof A) { // narrowed to A.
obj1.foo;
obj1.bar;
}
var obj2: any;
if (obj2 instanceof A) { // can't type narrowing from any.
obj2.foo;
obj2.bar;
}
// with generics
interface BConstructor {
new <T>(): B<T>;
}
interface B<T> {
foo: T;
}
declare var B: BConstructor;
var obj3: B<string> | A;
if (obj3 instanceof B) { // narrowed to B<string>.
obj3.foo = "str";
obj3.foo = 1;
obj3.bar = "str";
}
var obj4: any;
if (obj4 instanceof B) { // can't type narrowing from any.
obj4.foo = "str";
obj4.foo = 1;
obj4.bar = "str";
}
// has multiple constructor signature
interface CConstructor {
new (value: string): C1;
new (value: number): C2;
}
interface C1 {
foo: string;
bar1: number;
}
interface C2 {
foo: string;
bar2: number;
}
declare var C: CConstructor;
var obj5: C1 | A;
if (obj5 instanceof C) { // narrowed to C1.
obj5.foo;
obj5.bar1;
obj5.bar2;
}
var obj6: any;
if (obj6 instanceof C) { // can't type narrowing from any.
obj6.foo;
obj6.bar1;
obj6.bar2;
}
// with object type literal
interface D {
foo: string;
}
declare var D: { new (): D; };
var obj7: D | string;
if (obj7 instanceof D) { // narrowed to D.
obj7.foo;
obj7.bar;
}
var obj8: any;
if (obj8 instanceof D) { // can't type narrowing from any.
obj8.foo;
obj8.bar;
}