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
})
游乐场链接
