Typescript,准确地表示类方法的行为

Typescript, accurately represent class method behavior

本文关键字:类方法 表示 地表 Typescript      更新时间:2023-09-26

我有几个接口和一个类,它接受一个可选的options对象:

interface asObject { a: number, b: number }
interface options { returnAs: string; }
interface IA {
    go(): string;
    go(o: options): string | asObject;
}
class A implements IA {
    public go(o?: options): string | asObject {
        if(o&& o.returnAs && typeof o.returnAs === 'string') {
            switch(o.returnAs) {
                case 'object':
                    return { a: 5, b: 7 };
                default:
                    return 'string';
            }
        }
    }
}

我得到的错误是:"类A错误地实现了接口IA"。

如果我尝试重载一个方法:

...
public go(): string;
// Notice the parameter is no longer optional, ? removed.
public go(o: options): string | asObject { /* implementation as above */ }
...

现在我得到:"重载签名与函数实现不兼容"。

我知道我可以删除IA接口上的重载签名,并删除A类上的重载方法:

// Interface IA, notice the parameter is now optional, ? added.
go(o?: options): string | asObject;
// Class A
public go(o?: options): string | asObject { /* implementation as above */ }

让我解释一下:

A类有一个叫做go的方法,如果go没有提供options对象,它将返回一个字符串,但是如果用户提供了一个options对象,返回值取决于returnAs字段,意思是字符串或对象。

我的问题:

我认为我提供的解决方案不能准确地代表go方法的行为。

是否有一种方法来保留准确的行为,为了typescript的使用,而不是得到错误,因为我做了我上面描述的前2次尝试?

我说的准确行为是指:

我正在寻找一种方式,typescript将能够推断AObject类型为string:

var AObject = new A().go();

并且它将能够推断AObjectstringasObject:

var AObject = new A().go({ returnAs: 'object|string' });

我不能百分之百肯定这在打字稿中是可能的,如果是那样的话,我很高兴能得到建议。

最简单的方法是将A.go的结果声明为any:

public go(o?: options): any {

或声明函数接口:

interface asObject { a: number, b: number }
interface options { returnAs: string; }
interface IGoFunction {
    (): string;
    (o: options): string | asObject;
}
interface IA {
    go: IGoFunction;
}
class A implements IA {
    go = <IGoFunction>function (o?: options): string | asObject {
        if (o && o.returnAs && typeof o.returnAs === 'string') {
            switch (o.returnAs) {
                case 'object':
                    return { a: 5, b: 7 };
                default:
                    return 'string';
            }
        }
    }
}

实际上你甚至不需要声明命名接口:

class A {
    go = <{
        (): string;
        (o: options): string | asObject;
    }>function (o?: options): string | asObject {
    ...

缺点是该函数被添加到A的每个实例中,但是您可以显式地将它添加到prototype中:

class A {
    go: {
        (): string;
        (o: options): string | asObject;
    };
}
A.prototype.go = function (o?: options): any {
...