I want to create a generic function to work with the type of only passed argument and not any type from enum.
There is a simplified example.
enum EAnimals {
dog = 'dog',
cat = 'cat',
}
interface IConfig<T extends EAnimals> {
animalType: T
}
const configs: {[animalType in EAnimals]: IConfig<animalType>} = {
[EAnimals.cat]: {
animalType: EAnimals.cat
},
[EAnimals.dog]: {
animalType: EAnimals.dog
}
}
function doSomething<T extends EAnimals> (animalTypeParam: T): IConfig<T> {
return configs[animalTypeParam]; // error
}
I've seen 60730845 but I want to use generics. For example, here it's impossible to create config for EAnimals.cat
where animalType
is anything besides EAnimals.cat
. If I do doSomething(EAnimals.cat)
I will know that returned config is for cat
. If I declare parameter's type as animalTypeParam: EAnimals
I will have to also declare returned type as IConfig<EAnimals>
which ruins the idea.
I understand that it happens because T extends ...
can be bigger than original enum but I couldn't find something like in
for generics.
The main purpose of this is to get rid of human factor when creating configs and functions to work with this configs. If there's another working solution, I would love to hear it.
Just overload it:
enum EAnimals {
dog = 'dog',
cat = 'cat',
}
interface IConfig<T extends EAnimals> {
animalType: T
}
const configs: { [animalType in EAnimals]: IConfig<animalType> } = {
[EAnimals.cat]: {
animalType: EAnimals.cat
},
[EAnimals.dog]: {
animalType: EAnimals.dog
}
}
function doSomething<T extends EAnimals>(animalTypeParam: T):IConfig<T>
function doSomething<T extends EAnimals>(animalTypeParam: T) {
return configs[animalTypeParam]; // ok
}
const result = doSomething(EAnimals.cat) // IConfig<EAnimals.cat>
UPDATE
There are two alternative ways to handle it.
First - make partial application
enum EAnimals {
dog = 'dog',
cat = 'cat',
}
interface IConfig<T extends EAnimals> {
animalType: T
}
const configs = {
[EAnimals.cat]: {
animalType: EAnimals.cat
},
[EAnimals.dog]: {
animalType: EAnimals.dog
}
} as const
type Config = { [animalType in EAnimals]: IConfig<animalType> }
const doSomething = <C extends Config>(config: C) => <T extends EAnimals>(animalTypeParam: T) => {
return config[animalTypeParam];
}
const result = doSomething(configs)(EAnimals.cat)
But, then you need to make configs
an immutable object.
Second - don create config variable at all
enum EAnimals {
dog = 'dog',
cat = 'cat',
}
interface IConfig<T extends EAnimals> {
animalType: T
}
type Config = { [animalType in EAnimals]: IConfig<animalType> }
const doSomething = <C extends Config>(config: C) => <T extends EAnimals>(animalTypeParam: T) => {
return config[animalTypeParam];
}
const result = doSomething({
[EAnimals.cat]: {
animalType: EAnimals.cat
},
[EAnimals.dog]: {
animalType: EAnimals.dog
}
})(EAnimals.cat)