I have a factory class which will return different classes which all implement a same interface but with different generic. However, my factory class is unable to determine the different generic type used by the interfaces.
For example, I have the following interfaces:
interface ICityData<T> {
data: T
}
interface LondonData {
a: string,
b: number
}
interface BerlinData {
c: string,
d: string
}
interface NewYorkData {
a: Record<any, any>,
e: number
}
interface ICityConcrete<T> {
retrieve(): T
}
I have the follow concrete classes which all implement the ICityConcrete
interface but uses a different generic type:
class LondonConcrete implements ICityConcrete<LondonData> {
retrieve(): LondonData {
return {
a: 'test',
b: 123
}
}
}
class BerlinConcrete implements ICityConcrete<BerlinData> {
retrieve(): BerlinData {
return {
c: 'abc',
d: 'efg'
}
}
}
class NewYorkConcrete implements ICityConcrete<NewYorkData> {
retrieve(): NewYorkData {
return {
a: {
name: 'test'
},
e: 321
}
}
}
Finally, I have a factory to create those classes depending on the city
provided as the argument:
enum City {
London,
NewYork,
Berlin
}
class MyFactory {
public static create(city: City): ICityConcrete<TDependsOnCity> { // TDependsOnCity could be LondonData, BerlinData or NewYorkData depending on the city argument
switch (city) {
case City.London:
return new LondonConcrete();
case City.Berlin:
return new BerlinConcrete();
default:
return new NewYorkConcrete();
}
}
}
const myCreatedInstance = MyFactory.create(City.London);
Here's the problem, I want myCreatedInstance
to be of type ICityConcrete<TDependsOnCity>
. In other words, the type should depend on the city
provided. If City.London
was provided in the argument, myCreatedInstance
should be of type ICityConcrete<LondonData>
.
I tried to rearrange things in different ways and most of them have ICityConcrete<any>
no matter what I use as city
.
How can I control the return type based on the argument provided in this case?
You could define and use a city type to constructor map:
const cityConstructors = {
[City.London]: LondonConcrete,
[City.Berlin]: BerlinConcrete,
[City.NewYork]: NewYorkConcrete,
};
class MyFactory {
public static create<TCity extends City>(city: TCity): InstanceType<(typeof cityConstructors)[TCity]>
public static create(city: City) {
return new cityConstructors[city]();
}
}
const myCreatedInstance = MyFactory.create(City.London) // LondonConcrete
Try to use overloads :
enum City {
London,
NewYork,
Berlin
}
interface ICityData<T> {
data: T
}
interface LondonData {
a: string,
b: number
}
interface BerlinData {
c: string,
d: string
}
interface NewYorkData {
a: Record<any, any>,
e: number
}
interface ICityConcrete<T> {
retrieve(): T
}
class LondonConcrete implements ICityConcrete<LondonData> {
retrieve(): LondonData {
return {
a: 'test',
b: 123
}
}
}
class BerlinConcrete implements ICityConcrete<BerlinData> {
retrieve(): BerlinData {
return {
c: 'abc',
d: 'efg'
}
}
}
class NewYorkConcrete implements ICityConcrete<NewYorkData> {
retrieve(): NewYorkData {
return {
a: {
name: 'test'
},
e: 321
}
}
}
class MyFactory {
public static create(city: City.Berlin): ICityConcrete<BerlinData>
public static create(city: City.London): ICityConcrete<LondonData>
public static create(city: City): ICityConcrete<NewYorkData>
public static create(city: City) {
switch (city) {
case City.London:
return new LondonConcrete();
case City.Berlin:
return new BerlinConcrete();
default:
return new NewYorkConcrete();
}
}
}
const myCreatedInstance = MyFactory.create(City.London) // ICityConcrete<LondonData>
const myCreatedInstance2 = MyFactory.create(City.Berlin) // ICityConcrete<BerlinData>
const myCreatedInstance3 = MyFactory.create(City.NewYork) // ICityConcrete<NewYorkData>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.