I guess you're committed to writing out both the enum
and the interface
, and then hoping TypeScript will warn you the interface
is missing keys from the enum
(or maybe if it has extra keys)?
Let's say you have
enum E { A = "a", B = "b", C="c"};
interface ISomething { a: string, b: number, c: OtherType};
You can use conditional types to make TypeScript figure out if any constituents of E
are missing from the keys of ISomething
:
type KeysMissingFromISomething = Exclude<E, keyof ISomething>;
This type should be never
if you don't have any keys missing from ISomething
. Otherwise, it will be one of the values of E
like E.C
.
You can also make the compiler figure out if ISomething
has any keys which are not constituents of E
, also using conditional types... although this is more involved because you can't quite manipulate enum
s programmatically in expected ways. Here it is:
type ExtraKeysInISomething = {
[K in keyof ISomething]: Extract<E, K> extends never ? K : never
}[keyof ISomething];
Again, this will be never
if you don't have extra keys. Then, you can force a compile-time error if either one of these are not never
, by using generic constraints along with default type parameters:
type VerifyISomething<
Missing extends never = KeysMissingFromISomething,
Extra extends never = ExtraKeysInISomething
> = 0;
The type VerifyISomething
itself is not interesting (it is always 0
), but the generic parameters Missing
and Extra
will give you errors if their respective default values are not never
.
Let's try it out:
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
and
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
and
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
So all that works... but it's not pretty.
A different hacky way is to use a dummy class
whose sole purpose is to scold you if you don't add the right properties:
enum E { A = "a", B = "b" , C = "c"};
class CSomething implements Record<E, unknown> {
a!: string;
b!: number;
c!: boolean;
}
interface ISomething extends CSomething {}
If you leave out one of properties, you get an error:
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'.
It doesn't warn you about extra properties, although maybe you don't care?
Anyway, hope one of those works for you. Good luck.