typescript - TypeScript 可变参数元组类型推断问题,用于运行一系列“结果”返回函数的函数
问题描述
(TypeScript 游乐场中的完整代码:链接。)
我有一个Result<T, E>
用于计算的类型,它要么以 type 的值成功,要么以 typeT
的错误失败E
。
type Result<T, E> =
| {ok: true, value: T}
| {ok: false, value: E};
我想编写一个辅助函数,它可以Result
按顺序运行 -returning 函数列表。如果任何函数失败,则停止处理并返回错误。如果全部成功,则返回成功值列表。
以下是专门为长度为 3 的列表实现该功能的方法:
function sequenceFixed<T1, T2, T3, E>(fns: [() => Result<T1, E>, () => Result<T2, E>, () => Result<T3, E>]): Result<[T1, T2, T3], E> {
const [f1, f2, f3] = fns;
const r1 = f1();
if (!r1.ok) return r1;
const r2 = f2();
if (!r2.ok) return r2;
const r3 = f3();
if (!r3.ok) return r3;
return {ok: true, value: [r1.value, r2.value, r3.value]};
}
我想使用 TypeScript 4.0 的可变元组类型来使它适用于任何长度的列表。(函数体本身不需要类型检查,只需要调用站点。)
这是我的两次尝试:
declare function sequenceGeneric1<T extends Array<unknown>, E>(
fns: Readonly<{[P in keyof T]: () => Result<T[P], E>}>,
): Result<[...T], E>;
declare function sequenceGeneric2<Fns extends Array<unknown>, E>(
fns: Fns,
): Result<{[P in keyof Fns]: ExtractResultSuccessType<Fns[P]>}, E>;
type ExtractResultSuccessType<Fn> = Fn extends () => Result<infer T, unknown> ? T : unknown;
如果我明确传递类型参数,则正确调用这些函数类型检查,但我不知道如何让 TypeScript 推断类型参数。
declare function f1(): Result<string, string>;
declare function f2(): Result<boolean, string>;
declare function f3(): Result<number, string>;
function checkFixed() {
return sequenceFixed([f1, f2, f3]);
}
function checkGeneric1() {
return sequenceGeneric1([f1, f2, f3]);
}
function checkGeneric2() {
return sequenceGeneric2([f1, f2, f3]);
}
function check() {
// ok
const c1: Result<[string, boolean, number], string> = checkFixed();
// ERROR: Type 'Result<(string | number | boolean)[], unknown>' is not assignable to
// type 'Result<[string, boolean, number], string>'.
const c2: Result<[string, boolean, number], string> = checkGeneric1();
// ERROR: Type 'Result<(string | number | boolean)[], unknown>' is not assignable to
// type 'Result<[string, boolean, number], string>'.
const c3: Result<[string, boolean, number], string> = checkGeneric2();
}
function checkGeneric1WithAnnontation(): Result<[string, boolean, number], string> {
return sequenceGeneric1<[string, boolean, number], string>([f1, f2, f3]);
}
解决方案
在这种情况下,走你的路线肯定更容易sequenceGeneric2
,你有一个与传入参数的类型直接对应的泛型参数;毕竟,编译器T
从 type 的值推断类型T
比编译器T
从 type 的值推断类型更直接Readonly<{[K in keyof T]: SomeFunction<T[K]>}>
。
在您的情况下,您正在朝着正确的方向前进Fns
,但是您仍然期望编译器能够推断出E
,这只是不适合这样做,原因与您无法很好T
地推断出元组的原因相同sequenceGeneric1
。
还有一件事:如果将数组文字传递给函数,编译器很有可能会为其推断出无序数组类型而不是元组类型。有一些方法可以向编译器提示您希望推断出元组类型。一种方法是使用可变元组表示法......所以fns: Fns
你可以写而不是写fns: readonly [...Fns]
。就可以是什么类型而言,这并没有太大的区别fns
,但它确实导致编译器更喜欢 tuple-typed Fns
。
所以,继续:让我们只有一个泛型类型参数,Fns
然后让编译器计算输出中的类型T
和类型E
:
declare function sequenceGeneric<Fns extends Array<() => Result<any, any>>>(
fns: readonly [...Fns],
): Result<
{ [P in keyof Fns]: ExtractResultSuccessType<Fns[P]> },
ExtractResultErrorType<Fns[keyof Fns]>
>;
在哪里
type ExtractResultSuccessType<Fn> = Fn extends () => Result<infer T, any> ? T : never;
type ExtractResultErrorType<Fn> = Fn extends () => Result<any, infer E> ? E : never;
我已将unknown
推断类型更改为,any
因为编译器更容易验证它。 unknown
是一种真正的顶级类型,有时以您并不真正想要的方式严格遵守协变/逆变。
让我们看看它是否有效:
function checkGeneric() {
return sequenceGeneric([f1, f2, f3]);
}
// function checkGeneric(): Result<[string, boolean, number], string>
看起来不错!
推荐阅读
- swift - 如何在 60 秒内为绘图形状设置动画?
- javascript - 如何使用 forEach() 方法查询 firebase 实时数据库
- authentication - Laravel 5.6 - 创建用户不工作
- postgresql - 条件表达式,postgresql,从case中获取结果
- django - 选择时的Django案例
- svg - 带有阿拉伯字母的 Fabric js 文本组件以错误的方式生成
- wordpress - Mixcloud api页脚小部件事件?
- python - 如何对包含列表的元组进行排序?
- php - 提取显示错误
- android - 清除4.1以下Android固件中的数据不删除任何数据