网站搜索

typescript3.0 - 我如何在 TypeScript 3+ 中正确地为通用元组剩余参数指定类型?


在 TypeScript 3.0 之前,为 N 元函数推断类型涉及为已知的泛型参数创建任意数量的函数重载,然后使用类似 any 类型的剩余参数编写函数实现。TypeScript 3.0 改变了这一点,使得泛型类型可以扩展 any[],它充当泛型元组。

这个变化是有道理的,并且对我来说用泛型参数本身也能很好地工作,但我无法像在3.0之前那样让类型流向返回类型。

这是一个例子:

const takeToPromise = <T>(obs$: Observable<T>): Promise<T> => 
    obs$.pipe(take(1)).toPromise();

export function resolveObservables(): Promise<any[]>;
export function resolveObservables<T1>(o1: Observable<T1>): Promise<[T1]>;
export function resolveObservables<T1, T2>(o1: Observable<T1>, o2: Observable<T2>): Promise<[T1, T2]>;
export function resolveObservables<T1, T2, T3>(o1: Observable<T1>, o2: Observable<T2>, o3: Observable<T3>): Promise<[T1, T2, T3]>;
// Etc on the overloads
export function resolveObservables(...obs: (Observable<any>)[]): Promise<any[]> {
   return Promise.all(obs.map(takeToPromise));
}

假设我只有 3 个重载(最多到 T3)。如果我通过将数组展开传入函数调用来使用 resolveObservables,在 TS 3.0 中,我会收到一个错误:

预期 0-3 个参数,但得到的是 0 个或更多。

我可以通过固定参数调用很好地使用这个,但一旦我尝试展开参数,就会出现错误。任何让它工作的尝试都以某种方式失败了。例如:

export function resolveObservables<T, U extends Observable<T>[]>(...obs: U): Promise<T[]>;

将无法捕获任何类型(类型是 {})。

有没有办法修改这些类型,以便我可以让类型流通而不失去使用展开参数的能力?在我使用展开语法的情况下,我并不真正关心类型,如果我关心的话,我想我可以直接使用元组类型。但根据我到目前为止的尝试,要么使用展开但失去类型,要么保留类型但无法使用展开。

我这里漏了什么?

答案#1

我们可以让这个在 3.0 上大部分正常工作,但要实现完整的元组映射支持,我们需要等到 3.1。

首先,你不需要额外的 T 参数,只需使用约束 U extends Observable<any>[] 即可。这将为 U 推断出正确的元组类型。

要提取结果元组类型,我们可以使用映射类型和条件类型从可观察对象中提取结果类型。这部分在 3.0 中几乎可以工作。在 3.0 中,映射类型会弄乱数组类型,因此只有索引操作在结果类型上仍然可行,所有方法的类型都会变为 never。3.1 会让元组和数组上的映射类型更符合预期(详细信息请阅读 PR)。

export function resolveObservables
    <U extends Observable<any>[]>(...obs: U): Promise<ExtractObservableTypes<U>> {
    return null as any;
}


type ExtractObservableTypes<T> = {
    [P in keyof T] : T[P] extends Observable<infer U> ? U : never
}
let o!: Observable<number>
let o2!: Observable<string>


resolveObservables(o, o2).then(r => {
    r[0].toExponential // r[0] is number
    r[1].substring // r[1] is string
})

游乐场链接