TypeScript - test method against interface, ie T['methodName']

Given interface an interface:

class A {
   method(x:number) : string
}

Given function

const b = (x: string) : number;

The goal - test the function versus the interface

Realisation

type InterfaceKey = {
    method: any
};

function typecheck<T, K extends keyof InterfaceKey >(imp: () => T, d: T[K]) {
 //                                   K cannot be used to index type T ^
    imp();
    return d;
}

typecheck(someclass, 555); // <-- type check _is actually works_ here

// or

function typecheck <T>(imp: () => T, d: any) {
    imp();
    if(!d) {
        let mock: T;
        mock.default = d; // default does not exists on type T 
        return mock;
    }
    return d;
}

Look like the first approach is the only way, and it even performs a type check(in Webstorm, not in tsc), but does not compile.

Related: https://github.com/Microsoft/TypeScript/issues/15768

2 answers

  • answered 2017-11-15 01:09 dbandstra

    Is this going in the right direction?

    class someclass {
      method(x: number): string { return null!; };
    }
    
    const b = (x: number): string => null!;
    
    type InterfaceKey = {
      method: any
    };
    
    function typecheck<T extends InterfaceKey>() {
      return <K extends keyof InterfaceKey>(d: T[K]) => {};
    }
    
    // should fail if signature of someclass's `method` doesn't match that of `b`
    typecheck<someclass>()(b);
    

  • answered 2017-11-15 05:17 Anton Korzunov

    //base interface
    interface MyInterface {
        method: any 
    }
    
    // check with defined method
    interface CheckD<T extends MyInterface> {
        all(keys: {[P in keyof T]?: T[P]}): Check<T>;
    
        onlyMethod<Ts extends {[K in keyof Ts]: Ts[K]} & T>(fn: Ts['method']): Check<T>;
    }
    
    // check without
    interface Check<T> {
        all(keys: {[P in keyof T]?: T[P]}): Check<T>;
    }
    
    type CCD<T extends MyInterface > = CheckD<T>;
    type CC<T> = Check<T>;
    
    // magic
    function uncast(a: any): any{
        return <any>a;
    }
    
    // function overloading
    function load<K extends MyInterface>(a: K): CCD<K>;
    function load<T>(a: T): CC<T>;
    
    // type guards
    function load<T, K extends MyInterface>(a: T|K): CCD<K> | CC<T>{
        if ((<K>a).method) return <CCD<K>>uncast(a);
        return <CC<T>> uncast(a);
    }
    
    // TEST
    
    var C1 = {
        test: a => a + 1,
        method: a => 5
    }
    
    var C2 = {
        test: a => a + 1,    
    }
    
    let a1 = load(C1);
    a1.all({ test: a => 1 });
    a1.onlyMethod(55); // type error
    
    let a2 = load(C2);
    a2.all({ test: (a,b) => 1 }); // type error
    a2.all({ test2: a => 1 });// type error
    a2.onlyMethod(55); //no such method
    

    So, actually, the result is quite understandable.