联合中没有正确推断出过多的属性:有什么解决方法吗?

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" }; 

问题

有没有办法定义 VersionedSaveable 以确保 rev 的存在,而 id 是多余的 属性 检查错误,因为它应该是?

如果不是,随着推理引擎的改进,是否有可能在未来版本的 Typescript 中消除像这样的运行时错误。

背景——乐观并发模型

当您在此乐观并发模型上保存项目时,您可以...

强制编译器不允许某些属性的唯一方法是将它们设置为可选和 never,如下所示:

TS Playground

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) */