键入具有任意数量的不同参数的函数
Typing a function with arbitrary number of distinct arguments
我有一个名为 pipeline()
的函数,它目前是这样输入的
function pipeline(firstFn: Function, ...fns: Function[]) {
let previousFn = firstFn()
for (const func of fns) {
previousFn = func(previousFn)
}
return previousFn
}
如果我用它来流水线处理一些函数
const add = (x: number) => (y: number) => x+y
const result = pipeline(
() => 4,
add(2),
add(4)
)
// 10
但是,对于这个解决方案,没有类型检查函数的参数类型是否匹配前面函数的 return 类型,并且 return 类型很简单 any
.
我意识到这不能完全键入(例如,Promise.all([])
在它放弃之前最多可以处理 10 个承诺)但我想对调用进行类型检查至少有几个论点。
一个简单的解决方案
在管道中每个函数的 return 类型相同的情况下,这可以很容易地通过泛型实现:
function pipeline<T>(firstFn: () => T, ...fns: ((arg: T) => T)[]): T {
let previousFn = firstFn()
for (const func of fns) {
previousFn = func(previousFn)
}
return previousFn
}
更完整的解决方案
我们首先需要了解的是Promise.all是如何做到的?
来自打字稿来源lib.es2015.promise.d.ts:
all<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>, T10 | PromiseLike<T10>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>;
all<T1, T2, T3, T4, T5, T6, T7, T8, T9>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>;
all<T1, T2, T3, T4, T5, T6, T7, T8>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>;
...
all<T>(values: (T | PromiseLike<T>)[]): Promise<T[]>;
他们为从 10 开始一直到通用包罗万象的每个元素的函数编写了一个重载。
这就是为什么 Promise.all 对 returned 数组进行严格类型化可以在 10 多个不同类型的元素中脱颖而出。
如果您想复制此行为,则需要付出同样的努力。以下是最多三个参数的情况:
interface PipelineInterface {
<T0, T1, T2>(a0: () => T0, a1: (a: T0) => T1, a2: (a: T1) => T2): T2;
<T0, T1>(a0: () => T0, a1: (a: T0) => T1): T1;
<T>(firstFn: () => T, ...fns: ((arg: T) => T)[]): T;
}
const pipeline: PipelineInterface = function (firstFn, ...fns) {
let previousFn = firstFn()
for (const func of fns) {
previousFn = func(previousFn)
}
return previousFn
}
const result = pipeline(
() => 4,
(a) => a + 'Hello'
)
// result has type string
const result2 = pipeline(
() => 4,
(a: string) => a + 'Hello'
)
// Argument of type '(a: string) => string' is not assignable to parameter of type '(a: number) => string'.
我有一个名为 pipeline()
的函数,它目前是这样输入的
function pipeline(firstFn: Function, ...fns: Function[]) {
let previousFn = firstFn()
for (const func of fns) {
previousFn = func(previousFn)
}
return previousFn
}
如果我用它来流水线处理一些函数
const add = (x: number) => (y: number) => x+y
const result = pipeline(
() => 4,
add(2),
add(4)
)
// 10
但是,对于这个解决方案,没有类型检查函数的参数类型是否匹配前面函数的 return 类型,并且 return 类型很简单 any
.
我意识到这不能完全键入(例如,Promise.all([])
在它放弃之前最多可以处理 10 个承诺)但我想对调用进行类型检查至少有几个论点。
一个简单的解决方案
在管道中每个函数的 return 类型相同的情况下,这可以很容易地通过泛型实现:
function pipeline<T>(firstFn: () => T, ...fns: ((arg: T) => T)[]): T {
let previousFn = firstFn()
for (const func of fns) {
previousFn = func(previousFn)
}
return previousFn
}
更完整的解决方案
我们首先需要了解的是Promise.all是如何做到的?
来自打字稿来源lib.es2015.promise.d.ts:
all<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>, T10 | PromiseLike<T10>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>;
all<T1, T2, T3, T4, T5, T6, T7, T8, T9>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>, T9 | PromiseLike<T9>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>;
all<T1, T2, T3, T4, T5, T6, T7, T8>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>, T4 | PromiseLike <T4>, T5 | PromiseLike<T5>, T6 | PromiseLike<T6>, T7 | PromiseLike<T7>, T8 | PromiseLike<T8>]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>;
...
all<T>(values: (T | PromiseLike<T>)[]): Promise<T[]>;
他们为从 10 开始一直到通用包罗万象的每个元素的函数编写了一个重载。
这就是为什么 Promise.all 对 returned 数组进行严格类型化可以在 10 多个不同类型的元素中脱颖而出。
如果您想复制此行为,则需要付出同样的努力。以下是最多三个参数的情况:
interface PipelineInterface {
<T0, T1, T2>(a0: () => T0, a1: (a: T0) => T1, a2: (a: T1) => T2): T2;
<T0, T1>(a0: () => T0, a1: (a: T0) => T1): T1;
<T>(firstFn: () => T, ...fns: ((arg: T) => T)[]): T;
}
const pipeline: PipelineInterface = function (firstFn, ...fns) {
let previousFn = firstFn()
for (const func of fns) {
previousFn = func(previousFn)
}
return previousFn
}
const result = pipeline(
() => 4,
(a) => a + 'Hello'
)
// result has type string
const result2 = pipeline(
() => 4,
(a: string) => a + 'Hello'
)
// Argument of type '(a: string) => string' is not assignable to parameter of type '(a: number) => string'.