首页 > 解决方案 > 如何在 typescript 3.0 中强制接口“实现”枚举的键?

问题描述

假设我有一些 enum E { A = "a", B = "b"}。我想强制某些接口或类型(为了便于阅读,我只提到接口)具有 E 的所有键。但是,我想分别指定每个字段的类型。因此, { [P in E]: any }甚至{ [P in E]: T }不是正确的解决方案。

例如,代码可能包含两个实现 E 的接口:

随着 E 在开发过程中的扩展,它可能变成:

几个小时后:

等等等等。因此,从https://www.typescriptlang.org/docs/handbook/advanced-types.html看来,它还不受支持。我缺少任何打字稿黑客吗?

标签: typescripttypesstrong-typing

解决方案


我猜你致力于写出 theenum和 the interface,然后希望 TypeScript 会警告你interface缺少来自 the 的键enum(或者如果它有额外的键)?

假设你有

enum E { A = "a", B = "b", C="c"};
interface ISomething { a: string, b: number, c: OtherType};

您可以使用条件类型让 TypeScript 确定E的 键是否缺少任何成分ISomething

type KeysMissingFromISomething = Exclude<E, keyof ISomething>;

never如果您没有缺少任何键,则此类型应该是ISomething. 否则,它将是Elike的值之一E.C

您还可以让编译器确定是否ISomething有任何不是 s 的组成部分的键E可以使用条件类型...尽管这涉及更多,因为您无法enum以预期的方式以编程方式完全操作 s。这里是:

type ExtraKeysInISomething = { 
  [K in keyof ISomething]: Extract<E, K> extends never ? K : never 
}[keyof ISomething];

同样,这将是never如果您没有额外的密钥。然后,如果其中任何一个不是never,则可以通过使用泛型约束默认类型参数来强制编译时错误:

type VerifyISomething<
  Missing extends never = KeysMissingFromISomething, 
  Extra extends never = ExtraKeysInISomething
> = 0;

类型VerifyISomething本身并不有趣(它总是0),但是泛型参数如果它们各自的默认值不是MissingExtra则会给您错误never

让我们试一试:

enum E { A = "a", B = "b", C = "c" }
interface ISomething { a: string, b: number, c: OtherType }
type VerifyISomething<
  Missing extends never = KeysMissingFromISomething,
  Extra extends never = ExtraKeysInISomething
  > = 0; // no error

enum E { A = "a", B = "b", C = "c" }
interface ISomething { a: string, b: number } // oops, missing c
type VerifyISomething<
  Missing extends never = KeysMissingFromISomething, // error!
  Extra extends never = ExtraKeysInISomething
  > = 0; // E.C does not satisfy the constraint

enum E { A = "a", B = "b", C = "c" }
interface ISomething { a: string, b: number, c: OtherType, d: 1} // oops, extra d
type VerifyISomething<
  Missing extends never = KeysMissingFromISomething,
  Extra extends never = ExtraKeysInISomething // error!
  > = 0; // type 'd' does not satisfy the constraint

所以所有这些都有效......但它并不漂亮。


另一种 hacky 方法是使用一个假人class,其唯一目的是在你不添加正确的属性时责骂你:

enum E { A = "a", B = "b" , C = "c"};
class CSomething implements Record<E, unknown> {
  a!: string;
  b!: number;
  c!: boolean;
}
interface ISomething extends CSomething {}

如果您遗漏了其中一个属性,则会收到错误消息:

class CSomething implements Record<E, unknown> { // error!
  a!: string;
  b!: number;
}
// Class 'CSomething' incorrectly implements interface 'Record<E, unknown>'.
// Property 'c' is missing in type 'CSomething'.

它不会警告您有关额外属性的信息,尽管您可能不在乎?


无论如何,希望其中一个对你有用。祝你好运。


推荐阅读