从函数数组创建元组类型的返回类型,以便我们可以使用该元组类型转换`map`的结果

时间:2019-05-16 04:20:40

标签: typescript types

在回答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修饰符有关。

1 个答案:

答案 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;