¿Hay alguna forma de generar interfaces dinámicamente o inferir tipos a partir de métodos de clase? MWE a continuación con MyInterface creado manualmente desde MyClass: // Clase de destino class MyClass {public stringToNumber ...

0
gyllenfrans 15 mar. 2021 a las 00:35

1 respuesta

La mejor respuesta

Puede crear el tipo que desee utilizando tipos asignados y infer / extends:

type MyClassMethods<T> = {
    [K in keyof T]: T[K] extends (input: infer I) => infer O ? [I, O] : never;
}

Usando esto en tu clase

type MyInterface = MyClassMethods<MyClass>

Resuelve a

type MyInterface = {
    stringToNumber: [string, number];
    numberToString: [number, string];
}

Typescript Playground Link


Podemos mejorar esto para admitir métodos con más de un argumento. En realidad, no necesitamos el MyInterface. Solo necesitamos saber los nombres de los métodos. Hacemos que la función sea genérica que depende del nombre del método y usamos los tipos de utilidad incorporados Parameters y ReturnType para obtener los otros valores.

class MyClass {
    public someProp: number = 5;
    public stringToNumber = (value: string): number => {
        return Number(value);
    };
    public numberToString = (value: number): string => {
        return value.toString();
    };
    public add = (a: number, b: number): number => {
        return a + b;
    };
}

type MethodNames<T> = {
    [K in keyof T]: T[K] extends Function ? K : never;
}[keyof T]

const myClassFn = <
    K extends MethodNames<MyClass>
>(
    method: K,
    ...value: Parameters<MyClass[K]>
): ReturnType<MyClass[K]> => {
    return (new MyClass()[method] as any)(...value);
};

console.log( myClassFn("numberToString", 1) );
console.log( myClassFn("add", 1, 2));
console.log( myClassFn("stringToNumber", "5"));

Typescript Playground Link


Incluso podríamos aplicar esto a cualquier clase que se pueda construir sin argumentos. (Aunque realmente tiene más sentido que pasemos una instancia en lugar de un constructor).

const makeClassFn = <C extends {}>(constructor: new () => C) =>
    <K extends MethodNames<C>>(
        method: K,
        ...value: Parameters<C[K]>
    ): ReturnType<C[K]> => {
        return (new constructor()[method] as any)(...value);
    };

const myClassFn = makeClassFn(MyClass);

Typescript Playground Link

0
Linda Paiste 14 mar. 2021 a las 23:11