Firstly, a type is created that associates enum keys with types:
enum MyEnum {
A,
B
}
type TypeMap = {
[MyEnum.A]:string,
[MyEnum.B]:number
}
interface ObjInterface<T extends keyof TypeMap> {
obj: T,
objData: TypeMap[T]
}
interface SecondaryInterface {
value: string,
objChosen: ObjInterface<keyof TypeMap>
}
Then an object is made where objData's type gets verified against the TypeMap:
myObj:SecondaryInterface = {value:"", objChosen:{obj:MyEnum.A, objData:"a string"}}
This partly works but when I type objData, it gives a union type hint 'string | number' and not just 'string' because it infers the type from keyof TypeMap rather than the exact TypeMap[T].
Is it possible to get an exact type match for the enum key used and its associated type set in the type map?
It can be made to work using a type assertion but can this be made to work without one?:
myObj:SecondaryInterface = {value:"", objChosen:<ObjInterface<MyEnum.A>>{obj:MyEnum.A, objData:"a string"}}
According to your definition,
ObjInterface<MyEnum.A | MyEnum.B>is a single object type whereobjcan be anyMyEnumandobjDatacan bestring | number. But that's no what you want. You'd likeObjInterface<T>to distribute over unions inT, so thatObjInterface<MyEnum.A | MyEnum.B>is evaluated asObjInterface<MyEnum.A> | ObjInterface<MyEnum.B>. There are different ways to accomplish that, such as using distributive conditional types, but when the thing you want to distribute over is keylike, I usually prefer to write a distributive object type as coined in microsoft/TypeScript#47109. That's where you index into a mapped type.If you have a keylike type
K, and you want to distribute the type functionF<K>over unions inK, then you can write{[P in K]: F<P>}[K]. That turns into a mapped type with one property for each member ofK, which is immediately indexed into withKto make a union of those properties.For your code that looks like:
And then
ObjInterface<keyof TypeMap>evaluates toThis isn't strictly an
interfaceanymore, so maybe the name should be changed.Of course at this point you haven't actually shown a reason why you need
ObjInterfaceto continue to be generic, if all you care about is plugging inkeyof TypeMapthere. If you don't need that the generic, you can hardcodeKto bekeyof TypeMapand make itAnd then the rest of your code is
as desired.
Playground link to code