【问题标题】:How can I control the return type based on an argument in Typescript?如何根据 Typescript 中的参数控制返回类型?
【发布时间】:2021-07-08 10:26:07
【问题描述】:

我有一个工厂类,它将返回不同的类,这些类都实现了相同的接口但具有不同的泛型。但是,我的工厂类无法确定接口使用的不同泛型类型。

例如我有以下接口:

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
}

我有以下具体类,它们都实现了ICityConcrete 接口,但使用了不同的泛型:

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
        }
    }
}

最后,我有一个工厂来创建这些类,具体取决于作为参数提供的city

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);

问题来了,我希望 myCreatedInstance 的类型为 ICityConcrete&lt;TDependsOnCity&gt;。换句话说,类型应该取决于提供的city。如果在参数中提供了City.London,则myCreatedInstance 应该是ICityConcrete&lt;LondonData&gt; 类型。

我尝试以不同的方式重新排列事物,其中大多数都有ICityConcrete&lt;any&gt;,无论我使用什么作为city

如何根据本例提供的参数控制返回类型?

TS Playground

【问题讨论】:

    标签: typescript typescript-typings typescript-generics


    【解决方案1】:

    您可以定义和使用城市类型来构造地图:

    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
    

    Playground

    【讨论】:

    • 并且枚举可以替换为type City = keyof cityConstructors
    【解决方案2】:

    尝试使用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>
    

    Playground

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-13
      • 2021-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多