删除可选属性在类型断言检查后,同时保持静态分析。

huangapple go评论59阅读模式
英文:

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没有一种类型可以表示“类型为numbervalue属性是可选的(因此可以被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问题;所以您可能想要提交一个新的功能请求,以获取以这种方式工作的类型。但我怀疑它会被实现,因为这不是一个非常常见的情况,而解决方法是使用直接的类型守卫而不是自定义类型守卫函数。如果您决定提交功能请求,您应该小心地说明为什么现有的解决方法不够。

Playground代码链接

英文:

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.

Playground link to code

huangapple
  • 本文由 发表于 2023年6月12日 04:58:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/76452495.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定