我有zip具有签名的函数:
function zip<T, U, V>(ts: T[], us: U[], zipper: (t: T, u: U) => V): V[]
Run Code Online (Sandbox Code Playgroud)
我正在尝试为zipper参数分配一个默认值(t, u) => [t, u]:
function zip<T, U, V>(
ts: T[],
us: U[],
zipper: (t: T, u: U) => V = (t, u) => (<[T, U]>[t, u])
)
Run Code Online (Sandbox Code Playgroud)
这会产生(有点预期)关于(T, U) => [T, U]不可分配给(T, U) => V.
最后,我用一组有点难看的重载解决了这个问题:
export function zip<T, U>(ts: T[], us: U[]): [T, U][]
export function zip<T, U, V>(
ts: T[],
us: U[],
zipper: (t: T, u: U) => V
): V[]
export function zip<T, U>(
ts: T[],
us: U[],
zipper: (t: T, u: U) => [T, U] = (t, u) => [t, u]
): [T, U][] {
/* ... */
}
Run Code Online (Sandbox Code Playgroud)
这种方法有两个问题:
zip(T[], U[]): [T, U][]出现两次(第一次重载和实现本身);有没有更好的方法来做我想做的事?第一次尝试的错误是否是编译器错误(似乎不是,但如果是,它肯定会使解决方案更简单)?
这个错误是一个很好的错误;泛型类型参数由函数的调用者指定,而不是实现者。TypeScript 很乐意推断参数并免除开发人员指定它们的麻烦,但它们仍然是根据调用者的需要而不是实现者的需要来推断的。无论是谁调用手段zip()将被允许选择什么T,U以及V他们想要的。TypeScript 正确地警告您,该函数的实现不能假设它V与[T, U]. 使用原始签名和默认参数,调用者可以自由调用zip<string, number, boolean>(["a"],[1]). 是的,这太疯狂了,不,你不能实现它。因此,编译器正在帮助您解决警告。
重载是解决这个问题的合理方法。你的过载签名很好。至于实现签名,是的,你应该让它更通用,记住zipper参数必须是可选的。但是请注意,您必须断言默认值zipper返回 a V,因为编译器仍然无法根据实现签名保证这是真的(即使您知道它是安全的,因为重载限制了可以进行的调用)。下面是一个例子:
export function zip<T, U>(ts: T[], us: U[]): [T, U][]
export function zip<T, U, V>(
ts: T[],
us: U[],
zipper: (t: T, u: U) => V
): V[]
export function zip<T, U, V>(
ts: T[],
us: U[],
zipper?: (t: T, u: U) => V // note the question mark
): V[] {
if (!zipper) {
zipper = (t, u) => ([t, u] as any as V); // note the assertion
}
const ret: V[] = []
const len = Math.min(ts.length, us.length);
for (let i = 0; i < len; i++) {
ret.push(zipper(ts[i], us[i]));
}
return ret;
}
Run Code Online (Sandbox Code Playgroud)
这基本上就是 TypeScript 中的重载所发生的情况。编译器在检查实现时并不真正理解重载的签名。一旦你使用重载,你就告诉编译器你将负责确保实现是安全的。
无论如何,希望有所帮助;祝你好运!
| 归档时间: |
|
| 查看次数: |
493 次 |
| 最近记录: |