联合中没有正确推断出过多的属性:有什么解决方法吗?
Excess properties not inferred correctly in unions : any workarounds?
我遇到了 Typescript 的问题,它没有以尊重联合内共同约束的方式强制执行额外的 属性 检查。
如果联盟的任何分支允许该字段,似乎多余的 属性 检查会失败,即使实际的属性组合对于联盟中的任何特定分支都是非法的。
我的情况是否有解决方法,或者它最终可能会在 Typescript 中得到修复?
这不仅仅是理论上的 - 这种针对 typescript 中过多 属性 检查的糟糕联合分支扩展允许运行时错误未被编译器拾取,如 https://tsplay.dev/m35Aqw 所示 - 按 运行查看错误。
用例
我想要一个类型来添加属性来服务于乐观并发数据模型 - 一个可选的 id,或者一个可选的 id 和 rev 就像 couchdb。版本控制看起来像这样...
type Versioned = { id: string } | { id: string; rev: string };
我会用它使项目支持(可选添加)乐观并发字段,例如...
type Saveable<T> = T | (T & Versioned)
type Item = Saveable<{message:string}>
问题
不幸的是,我采用的方法允许具有 rev
但没有 id
的项目对编译器有效。
这些声明都应该被编译器接受...
const itemA: Item = { message: "hi" };
const itemB: Item = { message: "hi", id: "something" };
const itemC: Item = { message: "hi", id: "something", rev: "whatever" };
这应该是错误的,因为 rev
是任何没有 id
的分支的多余 属性...
const itemD: Item = { message: "hi", rev: "whatever" };
由于 属性 检查过多,这样的声明正确错误...
const itemE: Item = { message: "hi", extra: "whatever" };
问题
有没有办法定义 Versioned
或 Saveable
以确保 rev
的存在,而 id
是多余的 属性 检查错误,因为它应该是?
如果不是,随着推理引擎的改进,是否有可能在未来版本的 Typescript 中消除像这样的运行时错误。
背景——乐观并发模型
当您在此乐观并发模型上保存项目时,您可以...
- include both
id
和 rev
(你正在用 id
覆盖现有项目的最后一个已知修订版)
- include just
id
(这是一个新的项目,没有现有的修订,但它的 id
是由应用程序逻辑决定的)
- 不包含任何内容(
id
或 rev
- 这是一个没有现有修订的新项目,id
可以唯一分配给商店)
强制编译器不允许某些属性的唯一方法是将它们设置为可选和 never
,如下所示:
type Versioned = (
| { id: string; rev: string; }
| { id: string; rev?: never; }
| { id?: never; rev?: never; }
);
type Saveable<T> = T & Versioned;
type Item = Saveable<{message:string}>
// Valid
const itemA: Item = { message: "hi" };
const itemB: Item = { message: "hi", id: "something" };
const itemC: Item = { message: "hi", id: "something", rev: "whatever" };
// Invalid
const itemD: Item = { message: "hi", rev: "whatever" }; /*
^^^^^
Property 'id' is missing in type '{ message: string; rev: string; }'
but required in type '{ id: string; rev: string; }'.(2322) */
const itemE: Item = { message: "hi", extra: "whatever" }; /*
^^^^^^^^^^^^^^^^^
Object literal may only specify known properties,
and 'extra' does not exist in type 'Item'.(2322) */
我遇到了 Typescript 的问题,它没有以尊重联合内共同约束的方式强制执行额外的 属性 检查。
如果联盟的任何分支允许该字段,似乎多余的 属性 检查会失败,即使实际的属性组合对于联盟中的任何特定分支都是非法的。
我的情况是否有解决方法,或者它最终可能会在 Typescript 中得到修复?
这不仅仅是理论上的 - 这种针对 typescript 中过多 属性 检查的糟糕联合分支扩展允许运行时错误未被编译器拾取,如 https://tsplay.dev/m35Aqw 所示 - 按 运行查看错误。
用例
我想要一个类型来添加属性来服务于乐观并发数据模型 - 一个可选的 id,或者一个可选的 id 和 rev 就像 couchdb。版本控制看起来像这样...
type Versioned = { id: string } | { id: string; rev: string };
我会用它使项目支持(可选添加)乐观并发字段,例如...
type Saveable<T> = T | (T & Versioned)
type Item = Saveable<{message:string}>
问题
不幸的是,我采用的方法允许具有 rev
但没有 id
的项目对编译器有效。
这些声明都应该被编译器接受...
const itemA: Item = { message: "hi" };
const itemB: Item = { message: "hi", id: "something" };
const itemC: Item = { message: "hi", id: "something", rev: "whatever" };
这应该是错误的,因为 rev
是任何没有 id
的分支的多余 属性...
const itemD: Item = { message: "hi", rev: "whatever" };
由于 属性 检查过多,这样的声明正确错误...
const itemE: Item = { message: "hi", extra: "whatever" };
问题
有没有办法定义 Versioned
或 Saveable
以确保 rev
的存在,而 id
是多余的 属性 检查错误,因为它应该是?
如果不是,随着推理引擎的改进,是否有可能在未来版本的 Typescript 中消除像这样的运行时错误。
背景——乐观并发模型
当您在此乐观并发模型上保存项目时,您可以...
- include both
id
和rev
(你正在用id
覆盖现有项目的最后一个已知修订版) - include just
id
(这是一个新的项目,没有现有的修订,但它的id
是由应用程序逻辑决定的) - 不包含任何内容(
id
或rev
- 这是一个没有现有修订的新项目,id
可以唯一分配给商店)
强制编译器不允许某些属性的唯一方法是将它们设置为可选和 never
,如下所示:
type Versioned = (
| { id: string; rev: string; }
| { id: string; rev?: never; }
| { id?: never; rev?: never; }
);
type Saveable<T> = T & Versioned;
type Item = Saveable<{message:string}>
// Valid
const itemA: Item = { message: "hi" };
const itemB: Item = { message: "hi", id: "something" };
const itemC: Item = { message: "hi", id: "something", rev: "whatever" };
// Invalid
const itemD: Item = { message: "hi", rev: "whatever" }; /*
^^^^^
Property 'id' is missing in type '{ message: string; rev: string; }'
but required in type '{ id: string; rev: string; }'.(2322) */
const itemE: Item = { message: "hi", extra: "whatever" }; /*
^^^^^^^^^^^^^^^^^
Object literal may only specify known properties,
and 'extra' does not exist in type 'Item'.(2322) */