Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: type guards by constructor signature #1283

Closed
vvakame opened this issue Nov 26, 2014 · 11 comments
Closed

proposal: type guards by constructor signature #1283

vvakame opened this issue Nov 26, 2014 · 11 comments
Labels
Fixed A PR has been merged for this issue Help Wanted You can do this Suggestion An idea for TypeScript

Comments

@vvakame
Copy link
Contributor

vvakame commented Nov 26, 2014

What does we need?

We need type guards by constructor signature.

// class decomposed. from http://www.typescriptlang.org/Handbook#writing-dts-files
interface A_Static {
    new(m: any): A_Instance;
    st: string;
}
interface A_Instance {
    inst: number;
}
declare var A: A_Static;

// use type guards
var obj: A_Instance | Date;
if(obj instanceof A) {
  // I hope obj is narrowing to A_Instance. but A_Static does not have `prototype` property.
  obj.inst;
}

Motivation

almost .d.ts files does not have prototype property in DefinitelyTyped.
It means almost .d.ts files is not union types ready.
I think We can't enforce implement prototype property to .d.ts files.

Proposal

https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#420-type-guards

A type guard of the form x instanceof C, where C is of a subtype of the global type 'Function' and C has a property named 'prototype'

modified to

  • A type guard of the form x instanceof C, where C is of a subtype of the global type 'Function' and C has property named 'prototype' or C has a constructor signature.
    • when true,
      • narrows the type of x to the type of the 'prototype' property in C provided it is a subtype of the type of x, or
      • narrows the type of x to the type of the constructor signature's return type in C provided it is a subtype of the type of x, or

My english is not so good. I made sample code.

// only prototype
interface AStatic {
  (): void;
  prototype: AInstance;
}
interface AInstance {
  a: string;
}
declare var A: AStatic;
var objA: AInstance | Date;
if(objA instanceof A) {
  // narrows to AInstance
  // this pattern already possible!
  objA.a;
}

// only constructor signature
// this pattern is very popular in DefinitelyTyped.
interface BStatic {
  new (): BInstance;
}
interface BInstance {
  b: string;
}
declare var B: BStatic;
var objB: BInstance | Date;
if(objB instanceof B) {
  // narrows to BInstance
  objB.b;
}

// prototype + constructor signature
interface CStatic {
  new (): C1Instance;
  prototype: C2Instance;
}
interface C1Instance {
  c1: string;
}
interface C2Instance {
  c2: string;
}
declare var C: CStatic;
var objC: C1Instance | C2Instance | Date;
if(objC instanceof C) {
  // narrows to C2Instance. prototype is high priority
  objC.c2;
}

// constructor signature conflicted
interface DStatic {
  new (): D1Instance;
  new (value: any): D2Instance;
}
interface D1Instance {
  d1: string;
}
interface D2Instance {
  d2: string;
}
declare var D: DStatic;
declare var objD: D1Instance | D2Instance | Date;
if(objD instanceof D) {
  // narrows to D1Instance | D2Instance. union type of construtor signature's return types.
  (<D1Instance>objD).d1;
  (<D2Instance>objD).d2;
  // I hope this line to be error.
  (<Date>objD).getTime();
}
@RyanCavanaugh RyanCavanaugh added In Discussion Not yet reached consensus Suggestion An idea for TypeScript labels Nov 26, 2014
@RyanCavanaugh
Copy link
Member

I definitely get the motivation here.

One issue is that construct signatures can be generic and/or overloaded. We could probably use the union type of all the construct signatures' return types with {} filling in the type parameters.

@Igorbek
Copy link
Contributor

Igorbek commented Nov 26, 2014

It's definitely very useful suggestion. 👍

@johnnyreilly
Copy link

+1 for this - well expressed @vvakame

@basarat
Copy link
Contributor

basarat commented Nov 26, 2014

👍

@Bartvds
Copy link

Bartvds commented Nov 27, 2014

👍

1 similar comment
@Steve-Fenton
Copy link

👍

@vvakame
Copy link
Contributor Author

vvakame commented Nov 28, 2014

@RyanCavanaugh I was misunderstanding spec...

var array: number[] | Date;
if(array instanceof Array) {
    array.length;
}

I think this code evaluate to following pseudo code.

var array: number[] | Date;
if ( array instanceof Array ) {
    ( () => {
        // typeof Array.prototype are Array<any>.
        var _array: typeof Array.prototype = <number[]>array;
        ( () => {
            var array = _array;
            array.length;
        })();
    })();
}

but actual

var array: number[] | Date;
if ( array instanceof Array ) {
    ( () => {
        // https://github.com/Microsoft/TypeScript/blob/05fd2e9fb3db8c3af0248a9d16ca198944c91a7e/src/compiler/checker.ts#L4488
        // is this evaluate to {}[] ?
        // type mismatch! can't narrow types.
        var _array: getTypeOfSymbol( typeof Array.prototype ) = <number[]>array;
        ( () => {
            var array = _array;
            array.length;
        })();
    })();
}

I think this issue is not related to my proposal directly. but I feel "type guards with generics" is very important feature.


does we need contextual type guards?

class A<T> {
    data: T;
}

class AA<T1, T2> extends A<T1> {
    data1: T1;
    data2: T2;
}

var objA: AA<Date, RegExp> | Date;
if(objA instanceof AA) {
    // I wish this case narrows to AA<Date, RegExp>. type annotation of objA contains of it.
    objA.data1.getTime();
}
if(objA instanceof A) {
    // I wish this case narrows to A<Date>. type parameter picked by AA<Date, RegExp>. it contains type annotation of objA.
    objA.data.getTime();
}

class AB extends A<number> {
    data1: string;
}

var objB: AB | Date;
if(objB instanceof AB) {
    // narrows to AB.
    objB.data1.charAt(0);
}
if(objB instanceof A) {
    // I wish this case narrows to A<number>. type parameter picked by AB.
    objB.data.toFixed(2);
}
if(objB instanceof AA) {
    // I wish this case narrows to AA<number, {}>. (objB does not have AA types. smells of bug...)
}

var objC: AA<Date, RegExp> | AA<String, RegExp> | Date;
if(objC instanceof AA) {
    // narrows to AA<Date, RegExp> | AA<String, RegExp>;
    objC.data1;
    // I wish this case data1ObjC infered type Date | String; 
    var data1ObjC = objC.data1; 
}

off topic.

I'm not using VisualStudio.
I want to see type of variable like this.
playground_-_welcome_to_typescript

Is there any way use playgroud of master/HEAD in local environment?


off topic 2.

if Property_0_does_not_exist_on_type_1 occured after type narrowing check, I want get warning message ("{0} is not narrowed to {1}.") with it.

@danquirk
Copy link
Member

danquirk commented Dec 1, 2014

Unfortunately there's no easy way to get a playground with the latest bits. One hack you can do to check types via the command line is to redeclare the var whose type you are interested in. That should cause an error which will print the relevant types.

@vvakame
Copy link
Contributor Author

vvakame commented Dec 2, 2014

@danquirk thank you! It very useful information for me.

@RyanCavanaugh RyanCavanaugh added Help Wanted You can do this and removed In Discussion Not yet reached consensus labels Apr 27, 2015
@RyanCavanaugh RyanCavanaugh added this to the Community milestone Apr 27, 2015
@RyanCavanaugh
Copy link
Member

Approved

@mhegazy mhegazy added the Fixed A PR has been merged for this issue label May 6, 2015
@mhegazy mhegazy modified the milestones: TypeScript 1.5.2, Community May 6, 2015
@mhegazy
Copy link
Contributor

mhegazy commented May 6, 2015

Thanks @vvakame!

@mhegazy mhegazy closed this as completed May 6, 2015
@microsoft microsoft locked and limited conversation to collaborators Jun 18, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Fixed A PR has been merged for this issue Help Wanted You can do this Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

9 participants