Issue
Given
type UnionType = 'prop1' | 'prop2' | 'prop3';
type DerivedType = {
prop1: string;
prop2: number;
prop3: boolean;
};
Is there a way to declare DerivedType
so that if I add a member to UnionType
without adding the corresponding property to DerivedType
I'll get a TypeScript error?
If the types of the properties were all the same this would be trivial, but they aren't.
I don't want Record<UnionType, any>
- I want specific types for each property.
The actual use case involves a union type that is keyof
a different type; the "derived" type specifies configurations for each of the properties. The goal is to keep all of these types in sync.
Solution
Assuming you can't or don't want to refactor so that UnionType
is defined in terms of DerivedType
or so that both UnionType
and DerivedType
are defined in terms of something else, you can write your own CheckKeys<K, T>
utility type which will evaluate to T
, but issue a compiler error unless the keys of T
are seen to be exactly K
with nothing missing or extra (although there might be edge cases).
Here's one way to do it:
type CheckKeys<
K extends PropertyKey,
T extends Record<K, any> & Record<Exclude<keyof T, K>, never>
> = T;
Here T
is constrained so that it definitely has all of K
as keys (since it extends Record<K, any>
using the Record
utility type), and also (via intersection) any keys of T
that are not in K
(using the Exclude
utility type) must have properties of the impossible never
type. Since only never
is assignable to never
and you're not likely to set property types to never
to begin with, this should more or less enforce the restriction you're looking for. Let's test it:
type UnionType = 'prop1' | 'prop2' | 'prop3';
type DerivedType = CheckKeys<UnionType,
{ prop1: string; prop2: number; prop3: boolean; }
>; // okay
type ExtraKey = CheckKeys<UnionType,
{ prop1: string, prop2: number, prop3: boolean, prop4: Date } // error!
// Types of property 'prop4' are incompatible.
>
type MissingKey = CheckKeys<UnionType,
{ prop1: string, prop2: number } // error!
// Property 'prop3' is missing.
>
Looks good. DerivedType
is still the same type as before, and it compiles without error. But if you add or remove a key, you get the requisite compiler error mentioning where the problem is.
Answered By - jcalz
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.