Reputation: 4545
I have an Enum:
enum SomeEnumIds {
nameOne = 0,
nameTwo = 1,
nameThree = 2,
nameFour = 3,
}
and a derived Type:
export type EnumNames = keyof typeof SomeEnumIds;
and I want to define a type something like this:
export type EnumValues = {
nameOne: string;
nameTwo: boolean;
nameThree: Date
}
but I want to ensure that all the property names are of type EnumNames.
Does anyone have any suggestions?
I tried this:
export interface EnumValues extends Record<EnumNames, any> {
nameOne: string;
nameTwo: boolean;
nameThree: Date
}
but while this results in a compile error:
export interface EnumValues extends Record<string, Date> {
nameOne: string;
}
this does not:
export interface EnumValuesThree extends Record<EnumNames, any> {
notValid: string;
}
The ultimate goal is to have a class like this:
class SomeClass<TypeLookup extends Partial<Record<EnumNames, any>>>{
getValue<T extends EnumNames>(name: T): TypeLookup[T]{
return {} as any;
}
}
where a type is passed that defined the return types of the getValue function. It should only be possible to define known property values on that type but it must be possible to pass in a partial type.
Upvotes: 1
Views: 38
Reputation: 187144
Preventing extra properties can be tricky, and Typescript doesn't like to do it.
I think this may be your best bet:
class SomeClass<TypeLookup extends Partial<Record<EnumNames, unknown>>>{
getValue<T extends keyof TypeLookup & EnumNames>(name: T): TypeLookup[T]{
return {} as any;
}
}
Now this works as expected:
new SomeClass<{ nameOne: string }>().getValue('nameOne') // fine
new SomeClass<{ nameOne: string }>().getValue('nameThree') // error
new SomeClass<{ notValid: string }>() // error
All I changed with the constraint of T
in getValue
. Now it must be a key of the TypeLookup
generic parameter, intersected with the keys of the original enum. This means T
will always be a subset of SomeEnumIds
keys and the keys of type passed in.
One caveat though is that the extra keys are only reject if passed in directly. But because getValue
is constrained to the keys of the original enum, you won't be able to access those properties with the getValue
method.
type SomeType = { nameOne: string, notValid: string }
new SomeClass<SomeType>().getValue('nameOne') // fine
new SomeClass<SomeType>().getValue('notValid') // error
This is probably the best your going to get since I think at best you could make new SomeClass<SomeType>
resolve to never with some clever exact types but that would only throw type errors downstream from that.
Upvotes: 1