Search code examples
typescript

Modify TypeScript generic function parameters and return types


Can I make all the arguments of a function readonly and convert that function to an async function?

For example:

declare function foo<T>(bar: T, baz: T[]): T;

Convert to

declare function foo<T>(bar: Readonly<T>, baz: readonly T[]): Promise<T>;

I've tried this, which works for non-generic functions:

type ReadonlyArrayItems<T> = { [P in keyof T]: Readonly<T[P]> };
type MakeFunctionAsync<T> = (...args: ReadonlyArrayItems<Parameters<T>>) => Promise<ReturnType<T>>;
type Foo = MakeFunctionAsync<typeof foo>;

However, the new function type Foo messes up the generic function by turning all the original generic type T into unknown.


P.S. Thanks for the answers.

However, is it possible to do this for all functions within an interface?

For example:

interface Methods {
    foo<T>(bar: T, baz: T[]): T;
    bar(baz: string): void;
}

Become this

interface Methods {
    foo<T>(bar: Readonly<T>, baz: readonly T[]): Promise<T>;
    bar(baz: string): Promise<void>;
}

If add another type based on the above:

type MakeFunctionsAsync<T> = {
    [functionName in keyof T]: MakeFunctionAsync<T[functionName]>;
};

There doesn't seem to be a place to fill in the generic type parameter.

type MakeFunctionsAsync<T, U> = {
    [functionName in keyof T]: MakeFunctionAsync<T[functionName]<U>>;
};

This won't work.


Solution

  • You should add a generic param to the Foo:

    declare function foo<T>(bar: T, baz: T[]): T;
    
    type ReadonlyArrayItems<T> = { [P in keyof T]: Readonly<T[P]> };
    type MakeFunctionAsync<T extends (...args: any[]) => any> = (...args: ReadonlyArrayItems<Parameters<T>>) => Promise<ReturnType<T>>;
    type Foo<T> = MakeFunctionAsync<typeof foo<T>>;
    

    Playground

    To transform an interface you could use this (note that you need to have a generic interface to transform):

    type ReadonlyArrayItems<T> = { [P in keyof T]: Readonly<T[P]> };
    type MakeFunctionAsync<T extends (...args: any[]) => any> = (...args: ReadonlyArrayItems<Parameters<T>>) => Promise<ReturnType<T>>;
    
    interface Methods<T>{
        foo(bar: T, baz: T[]): T;
        bar(baz: string): void;
    }
    
    type MakeFunctionsAsync<T extends Record<PropertyKey, any>> = {
        [functionName in keyof T]: MakeFunctionAsync<T[functionName]>;
    };
    
    type Foo<T> = MakeFunctionsAsync<Methods<T>>;
    
    const foo: Foo<string> = {
        async foo(bar, baz){
            return '';
        },
        async bar(baz){
            return;
        }
    }
    

    Playground