Suppose I have some enum E { A = "a", B = "b"}
.
I would like to force some interfaces or types(for the sake of readability I'll mention only interfaces) to have all the keys of E. However, I want to specify the type of each field separately. Therefore, { [P in E]: any }
or even { [P in E]: T }
aren't proper solutions.
For instance, the code may contain two interfaces implementing E:
E { A = "a", B = "b"}
Interface ISomething { a: string, b: number}
Interface ISomethingElse { a: boolean, b: string}
As E extends during development it might become:
E { A = "a", B = "b", C="c"}
Interface ISomething { a: string, b: number, c: OtherType}
Interface ISomethingElse { a: boolean, b: string, c: DiffferntType}
And a few hours later:
E { A = "a", C="c", D="d"}
Interface ISomething { a: string, c: ChosenType, d: CarefullyChosenType}
Interface ISomethingElse { a: boolean, c: DiffferntType, d: VeryDifferentType}
And so on and so forth. Hence, from https://www.typescriptlang.org/docs/handbook/advanced-types.html it looks like it's not supported yet. Is there any typescript hack I am missing?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With