typescript - 如何在 typescript 3.0 中强制接口“实现”枚举的键?
问题描述
假设我有一些 enum E { A = "a", B = "b"}
。我想强制某些接口或类型(为了便于阅读,我只提到接口)具有 E 的所有键。但是,我想分别指定每个字段的类型。因此, { [P in E]: any }
甚至{ [P in E]: T }
不是正确的解决方案。
例如,代码可能包含两个实现 E 的接口:
E { A = "a", B = "b"}
Interface ISomething { a: string, b: number}
Interface ISomethingElse { a: boolean, b: string}
随着 E 在开发过程中的扩展,它可能变成:
E { A = "a", B = "b", C="c"}
Interface ISomething { a: string, b: number, c: OtherType}
Interface ISomethingElse { a: boolean, b: string, c: DiffferntType}
几个小时后:
E { A = "a", C="c", D="d"}
Interface ISomething { a: string, c: ChosenType, d: CarefullyChosenType}
Interface ISomethingElse { a: boolean, c: DiffferntType, d: VeryDifferentType}
等等等等。因此,从https://www.typescriptlang.org/docs/handbook/advanced-types.html看来,它还不受支持。我缺少任何打字稿黑客吗?
解决方案
我猜你致力于写出 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
. 否则,它将是E
like的值之一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
),但是泛型参数如果它们各自的默认值不是Missing
,Extra
则会给您错误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'.
它不会警告您有关额外属性的信息,尽管您可能不在乎?
无论如何,希望其中一个对你有用。祝你好运。
推荐阅读
- java - MongoDB Realm Studio 10.0.0 如何解密加密数据库?
- java - 后缀的中缀,我的程序已编译但不会产生输出。(爪哇)
- python - Python获取已关闭文件描述符的内容
- python - 如何使用“pip --user”方法“conda env create -f environment.yml”
- bash - 通过管道从标准输出中剪切和排序分隔日期
- timezone - 在 Luxon 中设置时间并以用户的本地时区显示
- sql - 在也被分组的列上聚合
- r - Tidyverse:If_else + str_length + str_pad 改变 1 列
- swift - Inout 和 NSMutableDictionary
- gnu-assembler - 在 ARM GAS 汇编器中正确使用 MOD 运算符