在回答Promise.all throwing type error when calling multiple async functions with different return types这个问题时,我可以解决这个难题。
我们具有以下三个功能:
const func1 = (): RegExp => { throw new Error(); }
const func2 = (): number => { throw new Error(); }
const func3 = (): Date => { throw new Error(); }
const funcs = [func1, func2, func3] as const;
我们要在该map
数组上运行funcs
,调用每个func
,并在返回的返回值数组中保持类型安全。
这有效:
type Results = [
ReturnType<typeof func1>,
ReturnType<typeof func2>,
ReturnType<typeof func3>,
];
const results = funcs.map(task => task()) as Results;
results[0].test('');
results[1].toExponential();
results[2].toTimeString();
此操作失败:
type MapToReturnType<T> = {
[K in keyof T]: T[K] extends () => any ? ReturnType<T[K]> : never;
};
// type TaskResultsToo = readonly [RegExp, number, Date]
type ResultsToo = MapToReturnType<typeof funcs>;
// Conversion of type '(number | RegExp | Date)[]' to type
// 'readonly [RegExp, number, Date]' may be a mistake because
// neither type sufficiently overlaps with the other. If this
// was intentional, convert the expression to 'unknown' first.
// Type '(number | RegExp | Date)[]' is missing the following
// properties from type 'readonly [RegExp, number, Date]': 0, 1, 2
const resultsToo = funcs.map(task => task()) as ResultsToo;
我们如何更改MapToReturnType<T>
以产生有效的Results
?我感觉到它与readonly
修饰符有关。
答案 0 :(得分:1)
问题是as const
产生了一个readonly
元组。由于MapToReturnType
是同态的,它将保留修饰符,因此,如果传入readonly
元组,则会得到readonly
元组。
由于map
产生了一个简单的可变数组,因此typescript不允许您直接将assert键入一个只读元组。
对我们来说,简单的解决方案是双重声明as unknown as ResultsToo
。优雅的解决方案是在映射时删除readonly
修饰符:
const func1 = (): RegExp => { throw new Error(); }
const func2 = (): number => { throw new Error(); }
const func3 = (): Date => { throw new Error(); }
const funcs = [func1, func2, func3] as const;
type MapToReturnType<T> = {
-readonly [K in keyof T]: T[K] extends () => any ? ReturnType<T[K]> : never;
};
// type TaskResultsToo = readonly [RegExp, number, Date]
type ResultsToo = MapToReturnType<typeof funcs>;
const resultsToo = funcs.map(task => task()) as ResultsToo;