typescript - 我们可以模拟泛型参数的“默认参数”吗?
问题描述
我构建了一个小型类型级库来简化形式的函数对的编写
function a(...): T | undefined;
function b(...): T;
其中 b 是从 a 派生的,如果返回 undefined 则抛出异常。基本思路如下:
export enum FailMode { CanFail, CanNotFail }
export const canFail = FailMode.CanFail;
export const canNotFail = FailMode.CanNotFail
export type Failure<F extends FailMode> = F extends FailMode.CanFail ? undefined : never;
export type Maybe<T, F extends FailMode> = T | Failure<F>;
export function failure<F extends FailMode>(mode: F): Failure<F> {
if (mode === canFail) return undefined as Failure<F>;
throw new Error("failure");
}
现在我们可以将a
and组合b
成一个函数,该函数需要一个额外的参数来区分类型:
function upperCaseIfYouCan<F extends FailMode>(x: string | undefined, mode: F): Maybe<string, F> {
if (x === undefined)
return failure<F>(mode);
return x.toUpperCase();
}
// both of these now work
let y: string = upperCaseIfYouCan("foo", canNotFail); // can throw, but will never return undefined
let z: string | undefined = upperCaseIfYouCan("foo", canFail); // cannot throw, but can return undefined
现在在大多数情况下,我想要这个canNotFail
变体,我想知道是否有一种方法可以使它成为“默认”,因为在这种情况下我不必传递canNotFail
参数,这样下面就可以工作了:
let y: string = upperCaseIfYouCan("foo"); // can throw
我已经确定这不能通过默认参数来实现,因为默认参数的类型必须与F
. 有没有办法以不同的方式实现这一点,这样定义类似的函数upperCaseIfYouCan
也同样容易?
解决方案
编译器对您指定默认参数不满意,因为总有人可以在调用时手动指定泛型类型参数,如下所示upperCaseIfYouCan<FailMode.CanFail>("");
:如果您不希望发生这种情况,您可以使用类型断言来抑制该错误,此外,您应该为F
类型参数提供自己的默认值,以便在没有明显选择F
的情况下编译器将选择您的默认值而不是完整的FailMode
:
function upperCaseIfYouCan<F extends FailMode = FailMode.CanNotFail>(
x: string | undefined,
mode: F = canNotFail as F
): Maybe<string, F> {
if (x === undefined)
return failure<F>(mode);
return x.toUpperCase();
}
这应该适用于您的用例:
let str = upperCaseIfYouCan("foo", canNotFail); // string
let strOrUndef = upperCaseIfYouCan("foo", canFail); // string | undefined
// this is the behavior you want
str = upperCaseIfYouCan("foo"); // string
另一方面,您可以只使用重载来处理调用函数的两种不同方式。虽然泛型在概念上更优雅,但在这种情况下重载可能更直观:
function upperCaseIfYouCan(x: string | undefined, mode: FailMode.CanFail): string | undefined;
function upperCaseIfYouCan(x: string | undefined, mode?: FailMode.CanNotFail): string;
function upperCaseIfYouCan(
x: string | undefined,
mode: FailMode = FailMode.CanNotFail
) {
if (x === undefined) return failure(mode);
return x.toUpperCase();
}
let str = upperCaseIfYouCan("foo", canNotFail); // string
let strOrUndef = upperCaseIfYouCan("foo", canFail); // string | undefined
str = upperCaseIfYouCan("foo"); // string
它在实现内部并不比带有类型断言的版本更安全,但至少不能轻易地以错误的方式调用重载(无需F
手动指定)。
好的,希望其中一个能给你一些指导。祝你好运!
推荐阅读
- mysql - MySQL 错误 1114:表已满
- javascript - 请解释 react.js 应用中上下文的使用
- c++ - 如何在 Visual Studio 2017 C++ 上添加预处理器定义?
- angular - Angular Material 概述了输入和 ngx-translate
- xml - 从日期输入面板格式化日期
- reactjs - Rect Styleguidist:意外的令牌导入
- pandas - 使用 pandas 将数据从给定日期列重新采样和插值到不同的日期列
- javascript - 更新对象多维数组的值
- google-apps-script - 创建工作表的副本并根据范围命名它们
- r - R - 选择顶级记录但有一个分组