Issue
Suppose I have a class, say, BasicModal
and one type describing BasicModal
's configurations, type BasicModalConfig = {}
for instance, and I also have a function, that takes the class and its settings a parameters, let's assume its signature is function openModal(component, configs);
. The goal is to infer the component's type (or class name if useful/possible) and use it in config
's type as a type parameter and set the configurations value accordingly, in other words, mapped types, but without the nested ternary madness.
Example of the desired result:
// This works
interface BasicModalConfig {
autoClose: boolean;
};
class BasicModal {}
// This would get out of hand very fast
type TypeResolver<K> = K extends BasicModal ? BasicModalConfig : never;
function openModal<T>(component: T, configs: TypeResolver<T>) {}
openModal(BasicModal, {
autoClose: true // this should give me hints/autocompletion
});
// This doesn't work
interface Resolver {
BasicModal: BasicModalConfig;
}
//or (doesn't compile)
//type Resolver = {
// [x: typeof BasicModal]: BasicModalConfig;
//};
function openModal2<T>(component: T, configs: Resolver[T extends keyof Resolver ? T : never]
) {}
openModal2(BasicModal, {
autoClose: true // this should give me hints/autocompletion, but config is "never"
});
I searched for something like "nameof
" from C#, in Typescript repository, to use it as a key value, but it's still in discussion: https://github.com/microsoft/TypeScript/issues/1579
As of typescript version 4.6, is it possible to even achieve this?
Here's a Typescript playground.
Solution
I can see two solutions, depending on how coupled you want the config and model to be.
You could add a field to the model that is the same type as the config and use conditional types to extract the config type:
type TypeResolver<K> = K extends { __config?: infer U } ? U : never;
function openModal<T>(component: T, configs: TypeResolver<T>) {}
openModal(BasicModal, {
autoClose: true // this should give me hints/autocompletion
});
You could also have a mapping between types, but since as you point out there is no nameof
in TS, you can use an object type for the value and extract from the values in the interface:
type TypeResolver<K> = Extract<TypeResolverMap[keyof TypeResolverMap], { model: K }>['config']
interface TypeResolverMap {
BasicModal: { model: typeof BasicModal; config: BasicModalConfig }
}
Or you could use a tuple for the mapping instead of an interface (although interfaces are open ended so you can add models from other files):
type TypeResolver<K> = Extract<TypeResolverMap[number], [K, any]>[1]
type TypeResolverMap = [
[typeof BasicModal, BasicModalConfig]
]
Or a mix between an interface and tuples:
type TypeResolver<K> = Extract<TypeResolverMap[keyof TypeResolverMap], [K, any]>[1]
interface TypeResolverMap {
BasicModal: [typeof BasicModal, BasicModalConfig]
}
Answered By - Titian Cernicova-Dragomir
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.