是否可以创建一个用户定义的类型保护,让编译器知道传递给它的所有参数都已定义?
我想做这样的事情:
public static all(...values: unknown[]): values is object[] {
return values.every(value => typeof(value) !== 'undefined');
}
我想使用它,以便我可以传递一组可能未定义的参数,处理它们未定义时发生的情况,否则将它们传递给需要值未定义的方法。
可能看起来像这样:
if (!ParamHelper.all(id, ...dateParts)) { return []; }
const date = new Date(dateParts.join('-'));
const result = await this.service.getData(assetId, date);
当我尝试上面的简单版本时,编译器告诉我:
类型谓词不能引用剩余参数
所以,我目前认为这是不可能完成的。但是,我想在放弃之前我应该问一下。
谢谢!
您可以将数组传递给函数,而不是使用其余参数:
public static all(values: unknown[]): values is object[] {
return values.every(value => typeof(value) !== 'undefined');
}
但是,该代码在技术上是不正确的,因为您断言这些值是对象,而您除了它们未定义之外一无所知。您应该检查 typeof 是否等于“object”,或者更正返回类型:
public static all(values: unknown[]): values is (string|number|object|symbol|boolean)[] {
return values.every(value => value !== null && typeof value !== 'undefined');
}
}
请注意,
typeof null === "object"
不幸的是,这两种比较都不会消除空值。我冒昧地在上面添加了一个空检查。
现在,如果您这样使用代码:
if (!ParamHelper.all([id, ...dateParts])) { return []}
const date = new Date(dateParts.join('-'));
然后您将鼠标悬停在第二行中的 dateParts 上,您将看到 typeguard 不起作用。令人沮丧的是
dateParts
仍然未定义。但很容易理解为什么,它可能会帮助您理解为什么它不适用于其余参数:
const checked = [id, ...dateParts];
if (!ParamHelper.all(checked)) return [];
const checkedDateParts = checked.slice(1);
const date = new Date(checkedDateParts.join('-'));
在这里,如果您将鼠标悬停在checked或checkedDateParts上,您会注意到它们是预期的类型。在上面的情况以及剩余参数的情况下,正在确定类型的数组在函数调用结束时被丢弃。 Typescript 还不够智能,无法将包含数组的类型追溯传递到其组成部分。鉴于这种逻辑可能是有条件的或迭代的,因此有理由期望它永远无法做到这一点。
在上面的示例中,我们保留了对数组的引用,因此也保留了类型。因此它有效。
我个人认为在这里使用类型保护是过度设计的。鉴于上下文是日期,您在评论中提供的代码:
if (!id || !day || !month || !year) { return; }
更清晰、更短、更易于维护。
如果你必须使用类型保护,我会选择类似的东西:
if (!id || !ParamHelper.all(dateParts)) { return }
const date = new Date(dateParts.join('-'));