英文:
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<string,number>` and everything works fine, property `b` exists on the this type. But last line throws error which says `Property 'b' does not exist on type 'y<string, number>'` Isn'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}
意味着参数的参数 必须 具有 a
和 b
成员,但 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<T,K>
toMyComplex<T,K>
- Add
type MyComplexWithoutB<T,K> = Omit< MyComplex<T,K>, 'b' >;
- Rename
y<T,K>
toMyComplex_or_MyComplexWithoutB<T,K>
- Change
MyComplex_or_MyComplexWithoutB<T,K>
's union toMyComplexWithoutB<T,K> | MyComplex<T,K>
Doing so gives us this:
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> ) {
}
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<string,number>
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<string,number> ) {
if( 'b' in arg ) {
// i.e. `arg` is `MyComplex<string,number>`
let { a, b } = arg;
}
else {
// i.e. `arg` is `MyComplexWithoutB<string,number>>`
let { a } = arg;
}
}
答案2
得分: 1
你看到的错误不是TypeScript的错误,而是y
类型定义方式的结果。
当你将myVariable
定义为y<string,number>
并包含b
属性时,TypeScript能够推断出正确的类型。然而,当你将myVariable
传递给一个期望y<string,number>
的函数时,TypeScript会检查类型兼容性,因为该函数可能接收一个类型为y<string,number>
但没有b
属性的对象,所以它会抛出一个错误。
解决这个问题的一种方法是使用类型守卫。这可以让你明确告诉TypeScriptb
属性存在。
function hasB<T, K>(obj: y<T, K>): obj is x<T, K> {
return obj.hasOwnProperty('b') ;
}
function myFunction(obj: y<string, number>) {
if (hasB(obj)) {
console.log(obj.b)
// obj具有b属性,因此我们可以安全地访问它
}
console.log(obj.a);
}
通常情况下,你不一定需要创建自己的类型守卫。TypeScript内置了用于检查类型是否与另一类型兼容的机制。例如,使用in
关键字来检查args对象是否有b
属性:
function myFunction(obj: y<string, number>) {
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<string,number>
and include the b
property, TypeScript is able to infer the correct type. However, when you pass myVariable to a function expecting a y<string,number>
, TypeScript checks the type compatibility, and because the function could receive an object of type y<string,number>
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<T, K>(obj: y<T, K>): obj is x<T, K> {
return obj.hasOwnProperty('b') ;
}
function myFunction(obj: y<string, number>) {
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<string, number>) {
if ('b' in obj) {
console.log(obj.b)
// obj has a b property, so we can safely access it
}
console.log(obj.a);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论