I'm creating a function whereby the first parameter is an array of "services", and the second parameter infers the return type of those services. For example:
const serviceOne = createService([], () => Promise.resolve({} as Service1));
const serviceTwo = createService([], () => Promise.resolve({} as Service2));
const serviceThree = createService(
[serviceOne, serviceTwo],
(one, two) => Promise.resolve({}), // one should be inferred as Service1, two should be inferred as Service2
);
How can I modify my function signature to achieve this inference? At the moment, I have them typed as any
function createService<T extends Service<any>[], U>(deps: T, factory: (...args: any[]) => Promise<U>) {
return {} as Service<U>;
}
type Service<U> = {
instance: U
};
CodePudding user response:
You can first need to change the constraint to T
to get typescript to infer a tuple type instead of an array type. To do this, we just need to add a union with the empty tuple to the constraint ([] |
). This will set typescript in the right state to infer tuples
We can then use a mapped type to map from a tuple containing Service<any>
to a tuple containing the service types:
const serviceThree = createService(
[serviceOne, serviceTwo],
(one, two) => Promise.resolve({} as Service3), // one is Service1, two is Service2
);
type GetServiceType<T extends Service<any>[]> = unknown [] & {
[P in keyof T]: T[P] extends Service<infer R> ? R: never
}
function createService<T extends [] | Service<any>[], U>(deps: T, factory: (...args: any[] & GetServiceType<T>) => Promise<U>) {
return {} as Service<U>;
}