Fel
Fel

Reputation: 4818

Typescript dynamic parameters and return type

I'm developing an Angular application that has to communicate with a backend that has some security restrictions that I have to deal with on every call. To facilitate the work with these calls, I've created a wrapper method like this:

type ApiResponse<T = any> = {
  status: number,
  data: T,
  error?: string
}

type ApiCall<T=any> = (...args: any[]) => ApiResponse<T>;

const makeApiCall = <T extends ApiCall>(apiCall: T, ...params: Parameters<T>): ReturnType<T> => {
  // Execute the "apiCall" method with the parameters, deal with restrictions, parse the response etc
}

So, let's say I have 2 API calls (they can have any number of parameters and types):

const f1: ApiCall<string> = (a: number, b: string) => {
  // Do logic
  return {
    status: 200,
    data: "Data sample"
  }
}

const f2: ApiCall<string[]> = (a: string, b: string, c: number, d: number) => {
  // Do logic
  return {
    status: 200,
    data: [ a, b ]
  }
}

I can use the wrapper to run them:

makeApiCall(f1, 2, 4);

makeApiCall(f2, "String", "Another", 10, 20);

However, nothing prevents me from sending wrong parameters number or type, like:

makeApiCall(f1, 2, 4, "WRONG PARAMETER");

makeApiCall(f2, "Wrong number and type of params", 10, true);

Would it be possible to have type safety with the above definitios so the Typescript compiler showed hints and errors when using the wrapper?

Thanks in advance,

Upvotes: 2

Views: 831

Answers (1)

Try to avoid declaring explicit generic parameters. TS should infer all types. Let him do the hard work :)

type ApiResponse<T> = {
    status: number,
    data: T,
    error?: string
}

type Fn = (...args: any[]) => any

const makeApiCall = <
    F extends Fn
>(apiCall: F, ...params: Parameters<F>): ReturnType<F> =>
    apiCall(...params)

const f1 = (a: number, b: string) => {
    // Do logic
    return {
        status: 200,
        data: [a, b]
    }
}

const f2 = (a: string, b: string, c: number, d: number) => {
    // Do logic
    return {
        status: 200,
        data: [a, b]
    }
}

/**
 * Ok
 */
// Return -> {  status: number;  data: (string|number)[];  }
const result1 = makeApiCall(f1, 42, 'str');

// Return -> {  status: number;  data: string[];  }
const result2 = makeApiCall(f2, "String", "Another", 10, 20);


/**
 * Errors
 */
makeApiCall(f1, 2, 4); // expected error


makeApiCall(f1, 2, 4, "WRONG PARAMETER"); // expected error

makeApiCall(f2, "Wrong number and type of params", 10, true); // expected error

Playground

Upvotes: 1

Related Questions