mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
type narrowing by constructor signiture of interface
This commit is contained in:
+31
-9
@@ -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;
|
||||
}
|
||||
+89
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user