如何在 TypeScript 中断言两个接口包含相同的键?

Law*_*eld 4 typescript typescript-generics

给定接口FooBar,我如何静态断言两种类型具有相同的键?

背景:我有一个接口,需要用作数据库 DTO 和“模型”类型。对于数据库 DTO,原语将是常规 JavaScript 原语。对于“模型”类型,原语将是名义类型(例如,AccountId而不是string等)。目前,我通过定义 2 个接口来实现这一目标:一个用于 DTO,一个用于模型。但是,我想要一些方法来确保这些接口不会漂移:

interface Dto {
  orderId: string;
  invoiceNo: number;
}
interface Model {
  orderId: OrderId;
  invoiceNo: InvoiceNumber;
}
assertKeysEqual<Dto, Model>() // Compiles OK
Run Code Online (Sandbox Code Playgroud)
interface Dto {
  orderId: string;
  invoiceNo: number;
  x: any;
}
interface Model {
  orderId: OrderId;
  invoiceNo: InvoiceNumber;
}
assertKeysEqual<Dto, Model>() // Compile ERROR ('Model' does not contain 'x')
Run Code Online (Sandbox Code Playgroud)
interface Dto {
  orderId: string;
  invoiceNo: number;
}
interface Model {
  orderId: OrderId;
  invoiceNo: InvoiceNumber;
  x: any;
}
assertKeysEqual<Dto, Model>() // Compile ERROR ('Dto' does not contain 'x')
Run Code Online (Sandbox Code Playgroud)

名义类型使用以下方法定义。然而,更一般地说:不同类型的按键类型的形状可能完全不同。例如,在一种类型中,您可能有order: OrderId,可以将其替换order: OrderEntity为其“扩展”对应类型。

export interface OrderId extends String {
  _OrderId: string;
}
export function OrderId(value: string): OrderId {
  return value as any;
}
Run Code Online (Sandbox Code Playgroud)

知道如何实施类似的事情assertKeysEqual吗?

Obl*_*sys 6

还有一个仅类型的解决方案,它提供了一些可读的错误消息:

type AssertKeysEqual<
  T1 extends Record<keyof T2, any>,
  T2 extends Record<keyof T1, any>
> = never

type Assertion = AssertKeysEqual<{a:1}, {a:1, b: 'x'}>
// ERROR: Property 'b' is missing in type '{ a: 1; }' but required in type 'Record<"a" | "b", any>'.

Run Code Online (Sandbox Code Playgroud)

TypeScript 游乐场

更新:在 TypeScript 4.2+ 中,如果您为记录引入额外的类型,则类型别名保留( docs)允许稍微令人愉快的错误消息:ShapeOf

type ShapeOf<T> = Record<keyof T, any>
type AssertKeysEqual<X extends ShapeOf<Y>, Y extends ShapeOf<X>> = never

type Assertion = AssertKeysEqual<{a:1}, {a:1, b: 'x'}>
// ERROR: Property 'b' is missing in type '{ a: 1; }' but required in 
// type 'ShapeOf<{ a: 1; b: "x"; }>'.

Run Code Online (Sandbox Code Playgroud)

TypeScript 游乐场