【问题标题】:TypeScript return type based on class property基于类属性的 TypeScript 返回类型
【发布时间】:2020-02-24 18:03:58
【问题描述】:

我想要一个不带参数的方法,该方法通常返回一个数组,或者当我知道只有一个项目时,只返回该数组的第一个元素。

注意:我无法更改 Thing 的形状或值,它来自外部应用程序,我只是围绕 Thing 的属性创建一个包装器。

interface Thing {
    [key: string]: any;
}

class Getter <T> {
    asArray: boolean;
    things: Thing[];
    prop: string;

    constructor(prop: string, things: Thing[], asArray: boolean) {
        this.asArray = asArray;
        this.things = things;
        this.prop = prop;
    }

    // THIS IS THE METHOD IN QUESTION
    getProp(): T | T[] {
        if (this.asArray) return things.map(t => t[this.prop]);
        else return things[0][this.prop];
    }

   // MORE METHODS ARE HERE FOR CHECKING DATA

}

class ThingModel {
    things: Thing[];
    asArray: boolean;

    constructor(asArray: boolean, ...things: Thing[]) {
        this.asArray = asArray;
        this.things = things;
    }

    get foo() {
        // We know the values of Thing.foo are numbers
        return new Getter<number>('foo', this.things, this.asArray);
    }

    get bar() {
        // We know the values of Thing.foo are strings
        return new Getter<string>('bar', this.things, this.asArray);
    }

}

const allThings = new ThingModel(true, ...getAllThings());
const oneThing = new ThingModel(false, getOneThing());

// TypeScript cannot infer these types, and both are the union of
// T | T[] even though we know which it will be, so the caller is
// forced to still make other assertions or type casts.
const allFooProps = allThings.foo.getProp(); // we know it's number[]
const oneBarProp = oneThing.bar.getProp(); // we know it's a string

如果我坚持总是返回一个数组,有些调用就不那么直观了。

// It's awkward that oneThing here would return an array of one property.
const [oneProp] = oneThing.foo.getProp();

我想我明白为什么这不能开箱即用 - TypeScript 可能无法保证 Getter.asArray 在构造函数之后不会被更改,但是,我尝试使用 asArray 的只读属性作为好吧,它没有帮助。

重写不起作用,因为我没有参数,并且我无法扩展类并重写方法,因为它抱怨返回类型不一样。我不能使用泛型,因为在没有首先检查 ThingModel.asArray 属性的情况下,我还不知道构造函数 new Getter(...) 的类型,并且 ThingModel 不能有泛型,因为各个属性的类型都是不同的。

我能想到的唯一方法是定义一个 SingleGetter 类并重新实现 Getter 的每个方法,然后在 ThingModel 中为每个属性执行 if/else 以构造适当的 Getter 或 SingleGetter。我想这可能是最“正确”的方法 - 但感觉就像很多多余的代码,Getter 已经有正确的逻辑,而不是返回 valuesvalues[0] 的开关。

有没有更简单的方法来帮助编译器知道我什么时候会得到一个值和一个数组?

【问题讨论】:

    标签: typescript class generics


    【解决方案1】:

    在接下来的内容中,我假设您最好重构以将 GetterThingModel 的“数组”版本与“单一”版本完全分开。

    您可以将asArray 属性设置为扩展boolean 的泛型类型A,以便编译器跟踪它是true 还是false

    所以你的Getter 类可能看起来像:

    class Getter<T, A extends boolean> {
        asArray: A;
        things: Thing[];
        prop: string;
    
        constructor(prop: string, things: Thing[], asArray: A) {
            this.asArray = asArray;
            this.things = things;
            this.prop = prop;
        }
    
        getProp(): A extends true ? T[] : T;
        getProp(): T | T[] {
            if (this.asArray) return this.things.map(t => t[this.prop]);
            else return this.things[0][this.prop];
        }
    }
    

    其中getProp() 的返回类型声明为T[]T,具体取决于A 的类型。那么你的ThingModel 也需要在A 中是通用的:

    class ThingModel<A extends boolean> {
        things: Thing[];
        asArray: A;
    
        constructor(asArray: A, ...things: Thing[]) {
            this.asArray = asArray;
            this.things = things;
        }
    
        get foo() {
            return new Getter<number, A>('foo', this.things, this.asArray);
        }
    
        get bar() {
            return new Getter<string, A>('bar', this.things, this.asArray);
        }
    }
    

    您可以看到我们是如何在foo()bar() 方法中将AThingModel 传递到Getter。然后您的后续代码将按照您的预期运行:

    allThings.foo.getProp().map(x => x.toFixed());
    oneThing.bar.getProp().toUpperCase();
    

    好的,希望对您有所帮助。祝你好运!

    Playground link to code

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-30
      • 2022-11-04
      • 1970-01-01
      • 2020-10-27
      • 2021-11-14
      • 2020-06-16
      • 2021-05-16
      • 1970-01-01
      相关资源
      最近更新 更多