我知道我们可以使用as
关键字在需要的地方内联Assert类型。但是我在这里要做的是改变代码块中变量的类型,使该类型在代码块退出之前一直存在。我希望在不创建单独的变量的情况下实现这一点,理想情况下不需要运行时Assert。
要求摘要
- 无
as
关键字类型Assert - 不创建单独的变量
- 无运行时Assert
示例
我尝试写的是一个setProps
方法,它通过一个函数来设置对象的属性。该函数的类型是通用的,这样就强制了正确的prop-to-value类型。函数内部是一个很大的switch
语句,它分别处理每个属性,每个属性可能需要多次访问值(因此我不想做as
Assert,因为它需要重复)。
更理想的情况是,我希望TypeScript隐式地推断switch语句中的值的类型,但我认为这在今天是不可能的。
下面是一个简单的例子,说明我正在努力实现的目标:
interface Props {
name: string;
age: number;
enrolled: boolean;
}
const props: Props = {
name: '',
age: 0,
enrolled: false,
};
export function setProp<K extends keyof Props>(prop: K, value: Props[K]): void {
const propName: keyof Props = prop; // This is needed because TypeScript can't break down the union within type K
switch (propName) {
case 'name':
props.name = value;
// ^-- Error!
// Type 'string | number | boolean' is not assignable to type 'string'.
// Type 'number' is not assignable to type 'string'.(2322)
break;
case 'age':
props.age = value;
// ^-- Same error!
break;
case 'enrolled':
props.enrolled = value;
// ^-- Same error!
break;
}
}
// Prop name and value type combination are enforced by TypeScript
setProp('name', 'John');
setProp('age', 20);
setProp('enrolled', true);
Playground
关闭解决方案
我想到的最接近的解决方案是使用一个完全未经检查的运行时Assert,并依靠目前许多捆绑器中存在的树抖动/死代码消除来删除它们:
export function uncheckedAssert<T>(value: unknown): asserts value is T {
return;
}
然后可以将函数重写为:
export function setProp<K extends keyof Props>(prop: K, value: Props[K]): void {
const propName: keyof Props = prop; // This is needed because TypeScript can't break down the union within type K
switch (propName) {
case 'name':
uncheckedAssert<Props[typeof propName]>(value);
props.name = value;
// ^-- No error!
break;
case 'age':
uncheckedAssert<Props[typeof propName]>(value);
props.age = value;
// ^-- No error!
break;
case 'enrolled':
uncheckedAssert<Props[typeof propName]>(value);
props.enrolled = value;
// ^-- No error!
break;
}
}
Playground
1条答案
按热度按时间7lrncoxx1#
从TypeScript 4.9开始,没有内置的类型Assert运算符可以按照你想要的方式在块作用域级别运行。请参见microsoft/TypeScript#10421了解相关的特性请求。解决这个问题的方法几乎是你已经找到的。
Assert函数基本上涵盖了能够以这种方式缩小某个变量的表观类型的用例。
如果能够显式地Assert变量的控制流类型,而不是必须在每个引用上强制转换,那肯定是很好的。调用
asserts
函数 * 可以 * 获得同样的效果,但当然,该调用在发出的代码中结束。尽管如此,JavaScript VM在减少调用空函数的开销方面还是相当不错的。大多数的请求都是作为ms/TS#10421的副本关闭的,并说明Assert函数是首选的解决方案。所以我想说在不久的将来这里不太可能有任何改变。不过,任何有兴趣看到这个的人可能会想给予这个问题一个评论👍和/或留下一个评论,描述他们的用例以及为什么Assert函数不能满足需要。