Union with omit

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

Union with omit

问题

以下是您要翻译的代码部分:

[查看以下代码片段][1]。它可能看起来有点奇怪,但在特定情况下非常有用。但为了问题的清晰,我以简化的形式发布它。

type x<T, K> = {
  a: T,
  b: (a: T) => K
};

type y<T, K> = Omit<x<T, K>, 'b'> | x<T, K>;

const myVariable: y<string, number> = { a: 'someString', b: (a: string) => 1 }

const myFunction = function ({ a, b }: y<string, number>) {}

最后两行对我来说看起来像TypeScript中的一个错误。myVariable 的类型是 y<string, number>,一切都正常,该类型上存在属性 b。但是最后一行却报错,错误信息为 Property 'b' does not exist on type 'y<string, number>',这不是矛盾吗?


<details>
<summary>英文:</summary>

[Look at the following snippet of code][1]. It might look strange but it is useful in specific cases. But for clarity of problem I post it in simplified form. 

type x<T,K> = {
a : T,
b : (a : T) => K
};

type y<T,K> = Omit<x<T,K>,'b'> | x<T,K>

const myVariable: y<string,number> = { a : 'someString',b : (a : string) => 1 }

const myFunction = function({a,b} : y<string,number>){}

Two last lines look to me like a bug in TypeScript. `myVariable` is of type `y&lt;string,number&gt;` and everything works fine, property `b` exists on the this type. But last line throws error which says `Property &#39;b&#39; does not exist on type &#39;y&lt;string, number&gt;&#39;` Isn&#39;t this a contradiction?


  [1]: https://www.typescriptlang.org/play?#code/FAFwngDgpgBAHgHgCoBoDSA%20GBeGBvYGGAQxgC4ZUZCYAjcmAClIqQEocs1gBfAbmrBQkWGGTosuAPIBbAJYgEiVJhQByWmqwAfeOMxDgAYwD2AOwDOIGDLAA1YgCc5xWgBsoFMVedmA5ihmAK4ytFCOkvhELDBqFiYyUADKIL5%20aij0FMwMPnL%20HNhYAIwwPEKmlta2AGJBZkYgcuY4MABm9Y3NZox4xJk8DN6p%20QHBoeEYbHjlQA

</details>


# 答案1
**得分**: 1

你的代码如果使用更具描述性的名称而不是简洁的加密名称,将更容易阅读:

* 将 `x<T, K>` 重命名为 `MyComplex<T, K>`
* 添加 `type MyComplexWithoutB<T, K> = Omit< MyComplex<T, K>, 'b' >;`
* 将 `y<T, K>` 重命名为 `MyComplex_or_MyComplexWithoutB<T, K>`
* 将 `MyComplex_or_MyComplexWithoutB<T, K>` 的联合更改为 `MyComplexWithoutB<T, K> | MyComplex<T, K>`

这样做会得到以下结果:

```typescript
type MyComplex<T, K> = {
  a: T,
  b: (a: T) => K
};

type MyComplexWithoutB<T, K> = Omit<MyComplex<T, K>, 'b'>;

type MyComplex_or_MyComplexWithoutB<T, K> = MyComplexWithoutB<T, K> | MyComplex<T, K>;

const myVariable: MyComplex_or_MyComplexWithoutB<string, number> = {
    a: 'someString',
    b: (a: string) => 1
}

const myFunction = function({a, b}: MyComplex_or_MyComplexWithoutB<string, number>) {
}

因此,再次查看解构参数时,使用 {a, b} 意味着参数的参数 必须 具有 ab 成员,但 MyComplex_or_MyComplexWithoutB<string, number> 类型 可能没有 b 成员,这意味着在任何情况下都不可能解构参数为 {a, b},因此会出现错误。

一个可能的解决方案是仅在你可以向 TypeScript 证明参数确实具有 b 成员时再进行解构:

const myFunction = function(arg: MyComplex_or_MyComplexWithoutB<string, number>) {
    if ('b' in arg) {
        // 即 `arg` 是 `MyComplex<string, number>`
        let {a, b} = arg;
    } else {
        // 即 `arg` 是 `MyComplexWithoutB<string, number>`
        let {a} = arg;
    }
}

...这个编译没有问题

英文:

Your code is easier to read if you use verbose, instead of short cryptic names:

  • Rename x&lt;T,K&gt; to MyComplex&lt;T,K&gt;
  • Add type MyComplexWithoutB&lt;T,K&gt; = Omit&lt; MyComplex&lt;T,K&gt;, &#39;b&#39; &gt;;
  • Rename y&lt;T,K&gt; to MyComplex_or_MyComplexWithoutB&lt;T,K&gt;
  • Change MyComplex_or_MyComplexWithoutB&lt;T,K&gt;'s union to MyComplexWithoutB&lt;T,K&gt; | MyComplex&lt;T,K&gt;

Doing so gives us this:

type MyComplex&lt;T,K&gt; = {
  a : T, 
  b : (a : T) =&gt; K
}; 

type MyComplexWithoutB&lt;T,K&gt; = Omit&lt; MyComplex&lt;T,K&gt;, &#39;b&#39; &gt;;

type MyComplex_or_MyComplexWithoutB&lt;T,K&gt; = MyComplexWithoutB&lt;T,K&gt; | MyComplex&lt;T,K&gt;;

const myVariable: MyComplex_or_MyComplexWithoutB&lt;string,number&gt; = {
    a : &#39;someString&#39;,
    b : (a : string) =&gt; 1
}

const myFunction = function( {a,b} : MyComplex_or_MyComplexWithoutB&lt;string,number&gt; ) {
}

And so looking at the destructured parameter again, by using {a,b} it means that the parameter's argument must have both a and b members, but the MyComplex_or_MyComplexWithoutB&lt;string,number&gt; type might not have a b member, which means destructuring the parameter to {a,b} under and and all circumstances is impossible, hence the error.


One possible solution is to destructure only after you can prove to TypeScript that the argument does have a b member:

const myFunction = function( arg: MyComplex_or_MyComplexWithoutB&lt;string,number&gt; ) {
    if( &#39;b&#39; in arg ) {
        // i.e. `arg` is `MyComplex&lt;string,number&gt;`
        let { a, b } = arg;
    }
    else {
        // i.e. `arg` is `MyComplexWithoutB&lt;string,number&gt;&gt;`
        let { a } = arg;
    }
}

...which compiles just fine.

答案2

得分: 1

你看到的错误不是TypeScript的错误,而是y类型定义方式的结果。

当你将myVariable定义为y&lt;string,number&gt;并包含b属性时,TypeScript能够推断出正确的类型。然而,当你将myVariable传递给一个期望y&lt;string,number&gt;的函数时,TypeScript会检查类型兼容性,因为该函数可能接收一个类型为y&lt;string,number&gt;但没有b属性的对象,所以它会抛出一个错误。

解决这个问题的一种方法是使用类型守卫。这可以让你明确告诉TypeScriptb属性存在。

function hasB&lt;T, K&gt;(obj: y&lt;T, K&gt;): obj is x&lt;T, K&gt; {
  return obj.hasOwnProperty(&#39;b&#39;) ;
}

function myFunction(obj: y&lt;string, number&gt;) {
  if (hasB(obj)) {
    console.log(obj.b)
    // obj具有b属性,因此我们可以安全地访问它
  }
  console.log(obj.a);
}

通常情况下,你不一定需要创建自己的类型守卫。TypeScript内置了用于检查类型是否与另一类型兼容的机制。例如,使用in关键字来检查args对象是否有b属性:

function myFunction(obj: y&lt;string, number&gt;) {
  if ('b' in obj) {
    console.log(obj.b)
    // obj具有b属性,因此我们可以安全地访问它
  }
  console.log(obj.a);
}
英文:

The error you are seeing is not a bug in TypeScript, but rather a result of the way the y type is defined.

When you define myVariable as y&lt;string,number&gt; and include the b property, TypeScript is able to infer the correct type. However, when you pass myVariable to a function expecting a y&lt;string,number&gt;, TypeScript checks the type compatibility, and because the function could receive an object of type y&lt;string,number&gt; that does not have the b property, it throws an error.

One solution to this issue is to use type guards. This allows you to explicitly inform TypeScript that the b property exists.


function hasB&lt;T, K&gt;(obj: y&lt;T, K&gt;): obj is x&lt;T, K&gt; {
  return obj.hasOwnProperty(&#39;b&#39;) ;
}

function myFunction(obj: y&lt;string, number&gt;) {
  if (hasB(obj)) {
    console.log(obj.b)
    // obj has a b property, so we can safely access it
  }
  console.log(obj.a);
}

In general, you don't necessarily need to create your own type guard. TypeScript has built-in mechanisms for checking if a type is compatible with another type. For example in keyword to checks whether the args object has a property b:

function myFunction(obj: y&lt;string, number&gt;) {
  if (&#39;b&#39; in obj) {
    console.log(obj.b)
    // obj has a b property, so we can safely access it
  }
  console.log(obj.a);
}

huangapple
  • 本文由 发表于 2023年2月19日 05:49:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/75496607.html
匿名

发表评论

匿名网友

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

确定