Search code examples
javascripttypescripttype-assertion

How to write typescript definition for is(constructor, instance) method


Let's say I have a is function, basically this is how it works.

is(Array, []) // true
is(String, '') // true

Coz it's a javascript function, i need to create a seperate typescript definition for it.

For now I can simply implement the interface below,

is(ctor: any, val: any): boolean;

But this definition is not good enough, coz after I run if(is(Array, stuffA)) { ... } the type of stuffA cannot be inferred by typescirpt compiler inside of the if block.

How can I make the type assertion works for the is function?


Solution

  • The signature should look like this:

    function is<T>(type: { new(): T }, obj: any): obj is T
    

    This will insure that the compiler understands the right type, that is this function is treated as a type guard.

    I have a function like that as well:

    function is<T>(obj: any, type: { new(): T }): obj is T {
        if (Number.isNaN(type as any) && Number.isNaN(obj)) {
            return true;
        }
    
        let objType: string = typeof obj;
        let nameRegex: RegExp = /Arguments|Function|String|Number|Date|Array|Boolean|RegExp/;
    
        if (obj && objType === "object") {
            return obj instanceof type;
        }
    
        let typeName: RegExpMatchArray = type.toString().match(nameRegex);
        if (typeName) {
            return typeName[0].toLowerCase() === objType;
        }
    
        return false;
    }
    

    (code in playground)


    Edit

    When using this function we'll always pass an instance and then the class.
    As we want the function to cover all types we'll use generics, where T is the type of the passed instance.

    If T is the type of the instance, then the type of the class is:

    { new(): T }
    

    That's the "typescript way" of describing a contrcutor object (which is also a class).
    More can be found here: Difference between the static and instance sides of classes.