我有两个对象,它们不应该有任何普通的叶子:

const translated = {
  a: {
    b: {
      c: "Hello",
      d: "World"
    }
  }
};

const toTranslate = {
  a: {
    b: {
      d: "Everybody"
    }
  }
};

上面的代码应该会产生一个错误:两个对象中都有‘a.b.d’.

我可以很容易地编写单元测试来判断这一点,但我试图生成一个很好的编译时错误消息.

我的try 是:

type Leaves<T> = T extends object ? { [K in keyof T]:
  `${Exclude<K, symbol>}${Leaves<T[K]> extends never ? "" : `.${Leaves<T[K]>}`}`
}[keyof T] : never

type TranslatedLeaves = Leaves<typeof translated>
type ToTranslateLeaves = Leaves<typeof toTranslate>

type Overlap = TranslatedLeaves & ToTranslateLeaves
type ErrorIfOverlapping<T extends never = Overlap> = void;

请参见:

不幸的是,这只给了我Type 'string' does not satisfy the constraint 'never'.

是否可以用所有公共的叶子生成一个错误消息?

TS Playground

推荐答案

TypeScrip实际上没有非常可定制的错误消息.在microsoft/TypeScript#23689有一个长期开放的特性请求,要求"无效"或"抛出"类型,这就像是never的味道,当遇到时会导致定制编译器警告.由于此功能不存在,因此有解决方法.

最简单的解决办法是编写一个包含您想要查看的消息的类型(通常是某种字符串literal typetemplate literal type,甚至tuple,混合了字符串文字和类型),并使其足够具体,以便在实践中只有never可以赋给它(您不会意外地生成兼容的类型).然后,您可以使用该类型来代替never,您将看到其中包含更多信息的内容.它仍然不会是一条pretty消息,你必须处理"type 'Foo' is not assignable to type '["Oops, I expected", Bar, "but got", Foo, "instead"]'"条消息,但希望它至少能为开发人员提供一些他们可能用来继续操作的东西.


对于您的示例代码:我们可以使用类型[`"${Overlap}" overlaps`],这是一个带有union条警告消息的单元素元组类型.Overlap可以赋值给[`"${Overlap}" overlaps`]的唯一合理方式是,如果Overlapnever(因为它可以赋值给任何东西).这表明:

type ErrorIfOverlapping<T extends [`"${Overlap}" overlaps`]
  = Overlap> = void;
//  ~~~~~~~
// Type 'string' does not satisfy the 
// constraint '["\"a.b.d\" overlaps"]'

这个错误不是最好的,但它提到了"a.b.d" overlaps,这应该足以帮助开发人员解决这个问题.

Playground link to code

Typescript相关问答推荐

如何将http上下文附加到angular中翻译模块发送的请求

TypeScript Page Routing在单击时不呈现子页面(React Router v6)

如何在使用`Next—intl`和`next/link`时导航

如何键入函数以只接受映射到其他一些特定类型的参数类型?

带占位符的模板文字类型何时扩展另一个?

我想创建一个只需要一个未定义属性的打字脚本类型

在不更改类型签名的情况下在数组并集上映射文字

对于始终仅在不是对象属性时才抛出的函数,返回Never

如何在React组件中指定forwardRef是可选的?

为什么&Quot;元素隐式具有';Any';类型,因为...即使我已经打字了,也不能作为索引显示错误吗?

NPM使用Vite Reaction应用程序运行预览有效,但我无法将其部署到Netlify

如何解决&Quot;类型不可分配给TypeScrip IF语句中的类型&Quot;?

使用强类型元组实现[Symbol.iterator]的类型脚本?

内联类型断言的工作原理类似于TypeScrip中的断言函数?

如何以类型安全的方式指定键的常量列表,并从该列表派生所挑选的接口?

为什么`map()`返回类型不能维护与输入相同数量的值?

Typescript 从泛型推断类型

TS2532:对象可能未定义

类型string不可分配给类型string| 联盟| 的'

从接口推断模板参数?