Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript iterate string union type

Tags:

typescript

Suppose I have:

class A {
 f():void{}
 g():void{}
}
const a = new A();

type AFunctions = "f" | "g";

I want to iterate AFunctions and produce new functions. Similar to mapped types but with implementation and without manually writing all keys of course.

Pseudo code

const b: A = {
 for F in AFunctions add function F() {
   return a[f]();
  }
}
like image 971
NN_ Avatar asked Feb 06 '18 14:02

NN_


1 Answers

You can iterate the properties of the class using Object.getOwnPropertyNames and Object.getPrototypeOf.

function mapFunctions<T>(type: new (... params: any[])=> T) : { [P in keyof T] : ()=> void } {
    let mapped: { [name: string] : ()=> void } = {}

    let p = type.prototype;
    while(p !=  Object.prototype) {
        for (const prop of Object.getOwnPropertyNames(p)) {
            if(prop == 'constructor') continue;
            if(typeof p[prop] !== 'function') continue;

            mapped[prop] = ()=> {
                console.log(prop);
            }
        }
        p = Object.getPrototypeOf(p);
    }

    return <any>mapped;
}
class A {
    f(): void { }
    g(): void { }
}

class B extends A{
    h(): void { }
    i(): void { }
}
let ss = mapFunctions(B); // has functions f,g,h,i

Tying the type of the function to the result of the function on the origin object is more difficult, Conditional types and their associated inference behavior in 2.8 will let you drill down to return type and argument types, but for now you could only use the full function type, such as { [P in keyof T] : ()=> T[P] } (for a function with no parameters that return a function with the same signature as the original) or { [P in keyof T] : (fn: T[P])=> void } (for a function that takes a function with the same signature as the original as a parameter)

like image 81
Titian Cernicova-Dragomir Avatar answered Oct 16 '22 03:10

Titian Cernicova-Dragomir