英文:
Delete optional property after type predicate check while maintaining static analysis
问题
我有一个只有可选属性的接口 I。在我的情况下,I 是一个泛型。我需要一个类型缩小函数,仍然允许删除缩小的属性。以下是代码示例:
interface I {
value?: number
}
function check(i: I): i is I & { value: number } {
return "value" in i
}
function test(i: I) {
if (check(i)) {
i.value += 1 // 可行
delete i.value // 错误:'delete' 运算符的操作数必须是可选的。
}
}
// i as I is not an option to force deletion because it allows the access
// to i.value after deletion
function test2(i: I) {
if (check(i)) {
i.value += 1 // 可行
delete (i as I).value // 可行
i.value += 1 // 仍然可行
}
}
// 期望的行为应该类似于
function test(v: I) {
if ("value" in v) {
v.value += 5 // 可行
delete v.value // 可行
v.value += 5 // 错误:易理解且良好
}
}
问题在于,缩小类型会创建一个新的带有 value 作为必需类型的接口。因此,我需要一种语法来表示 value 可以是可选的/可删除的,但当前包含一个值。
一种解决方法是定义一个用于删除的函数,该函数将值重新缩小为可选。目前没有直接支持 delete 关键字的语法或模式,可以实现这一目标。
英文:
I have an interface I with only optional properties. In my case I is a generic. I need a type narrowing function that still allows to delete the narrowed property. The following code shows the problem.
interface I {
value?: number
}
function check(i: I): i is I & { value: number } {
return "value" in i
}
function test(i: I) {
if (check(i)) {
i.value += 1 // ok
delete i.value // error: The operand of a 'delete' operator must be optional.
}
}
// i as I is not an option to force deletion because it allows the access
// to i.value after deletion
function test2(i: I) {
if (check(i)) {
i.value += 1 // ok
delete (i as I).value // ok
i.value += 1 // !!! still ok
}
}
// expected behaviour should be something like
function test(v: I) {
if ("value" in v) {
v.value += 5 // ok
delete v.value // ok
v.value += 5 // error: understandable and good
}
}
The problem is, that the narrowing creates a new interface with value as a required type. So I need syntax to say that value can be optional / removed but currently contains a value.
A workaround would be to define a function for deleting that renarrows the value back to optional.
Is there some syntax or pattern to allow the use of the delete keyword?
答案1
得分: 1
以下是您要翻译的内容:
很不幸,您无法通过其返回类型为i is X形式的自定义类型守卫函数来表达您正在进行的操作,其中X是某种类型。 TypeScript没有一种类型可以表示“类型为number的value属性是可选的(因此可以被delete),但已知存在(因此可以分配给number)”。类型{value?: number}表示该属性是可选的但可能缺失,而类型{value: number}表示该属性已知存在但是必需的。这两者都不适合用作自定义类型守卫函数的返回类型。
通过控制流分析,您可以实现所需的状态,正如您所看到的(尽管我会更改您的示例为:
function test(i: I) {
if (typeof i.value !== "undefined") {
i.value += 5 // ok
delete i.value // ok
i.value += 5 // error: understandable and good
}
}
而不是if ("value" in i))。但再次强调,没有适当的方法来表示通过检查后的状态为i赋予一个新的类型。
我没有找到相关的TypeScript GitHub问题;所以您可能想要提交一个新的功能请求,以获取以这种方式工作的类型。但我怀疑它会被实现,因为这不是一个非常常见的情况,而解决方法是使用直接的类型守卫而不是自定义类型守卫函数。如果您决定提交功能请求,您应该小心地说明为什么现有的解决方法不够。
英文:
Unfortunately you can't express what you're doing with a custom type guard function whose return type is of the form i is X for some type X. TypeScript doesn't have a type that means "the value property of type number is optional (so it may be deleted), but known to be present (so it can assigned to number)". The type {value?: number} means the property is optional but possibly missing, and the type {value: number} means the property is known to be present but is required. Neither is appropriate for the return type of a custom type guard function.
The desired state is possible to achieve via control flow anlysis as you've seen (although I'd change your example to
function test(i: I) {
if (typeof i.value !== "undefined") {
i.value += 5 // ok
delete i.value // ok
i.value += 5 // error: understandable and good
}
}
instead of if ("value" in i)). But again, there's no appropriate way to represent that post-checked state as giving i a new type.
I didn't find a relevant TypeScript GitHub issue; so you might want to file a new feature request for a type that works this way. But I doubt it would be implemented, since it's not a very common situation, and the workaround is to use the direct type guard instead of a custom type guard function. If you do decide to file a feature request, you should take care to demonstrate why available workarounds don't suffice.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论