Reputation: 32767
By using TypeScript, anyone using sequentially
should be forced to pass 1 to N parameters, which input Promise<T>
is equal to the output [Promise<T>,..
[Not working] I'm getting tslint errors in the first declaration when overloading it in the following way:
Overload signature is not compatible with function implementation. ts(2394)
promise.ts
function sequentially<T1>(promiseWrappers: [
() => Promise<T1>
]): Promise<[T1]>;
async function sequentially<T1, T2>(promiseWrappers: [
() => Promise<T1>,
() => Promise<T2>
]): Promise<[T1, T2]> {
const resolved = [];
for (const wrapper of promiseWrappers) {
resolved.push(await wrapper());
}
// @ts-ignore
return resolved;
}
[Working] Only way that seems to be working is when writing the implementation with .js
and a separate declaration file .d.ts
, like the following:
promise.js
export async function sequentially(promiseWrappers) {
const resolved = [];
for (const wrapper of promiseWrappers) {
resolved.push(await wrapper());
}
return resolved;
}
promise.d.ts
export declare function sequentially<T1, T2>(promiseWrappers: [
() => Promise<T1>,
() => Promise<T2>
]): Promise<[T1, T2]>;
export declare function sequentially<T1, T2, T3>(promiseWrappers: [
() => Promise<T1>,
() => Promise<T2>,
() => Promise<T3>
]): Promise<[T1, T2, T3]>;
How do I fix the first TS implementation case?
Upvotes: 3
Views: 471
Reputation: 51619
There are some rules for implementing overloaded functions in TypeScript:
Implementation signature is not taken into account at the call sites, it's used only for typechecking the implementation (mentioned at the end of the paragraph documenting overloads in the handbook)
Implementation signature must have parameters which are compatible with all overloaded signature declarations. In practice, it means that each parameter type must be a union type of all parameter types from all overloads for that parameter position.
Implementation signature must have return type which is an intersection of return types from all overloads. In practice, it means that the implementation must do type cast to that type in each return statement, or simply have implementation return type declared as any.
I can't find 2 and 3 documented anywhere, but they follow from general variance rules for function type compatibility and the fact that implementation signature must be compatible with all overloaded declarations.
Here is the code, which compiles with --strictFunctionTypes
and --noImplicitAny
turned on
function sequentially<T1>(promiseWrappers: [
() => Promise<T1>
]): Promise<[T1]>;
function sequentially<T1, T2>(promiseWrappers: [
() => Promise<T1>,
() => Promise<T2>,
]): Promise<[T1, T2]>;
async function sequentially<T1, T2>(promiseWrappers: [
() => Promise<T1>,
] | [
() => Promise<T1>,
() => Promise<T2>
]): Promise<[T1, T2] & [T1]> {
const resolved = [] as unknown as [T1, T2] & [T1];
for (const wrapper of promiseWrappers) {
resolved.push(await wrapper());
}
return resolved;
}
Upvotes: 3
Reputation: 3227
The overloads you may want to support may have nothing to do with eachother. To support this make the bottom-most signature all any
types. Something like this should work for you.
function sequentially<T1>(promiseWrappers: [
() => Promise<T1>
]): Promise<[T1]>;
function sequentially<T1, T2>(promiseWrappers: [
() => Promise<T1>,
() => Promise<T2>
]): Promise<[T1, T2]>;
async function sequentially(promiseWrappers: any[]): Promise<any[]> {
const resolved = [];
for (const wrapper of promiseWrappers) {
resolved.push(await wrapper());
}
// @ts-ignore
return resolved;
}
Upvotes: 2