Typescript child class function overloading

2019-04-25 11:41发布

How can I achieve something similar to this pattern in typescript?

class A {
    Init(param1: number) {
        // some code
    }
}

class B extends A {
    Init(param1: number, param2: string) {
        // some more code
    }
}

The code snipped above appears like it should work, however on close inspection of How Typescript function overloading works it makes sense that an error is thrown:

TS2415: 'Class 'B' incorrectly extends base class 'A'. 
Types of property 'Init' are incompatible.

I know that constructor functions allow this behaviour, but I can't use constructors here as these objects are pooled for memory efficiency.

I could provide another definition of Init() in class A:

class A {
    Init(param1: number, param2: string): void;
    Init(param1: number) {
        // some code
    }
}

However this is less than ideal as now the base class needs to know about all of its derived classes.

A third option would be to rename the Init method in class B but that would not only be pretty ugly and confusing, but leave exposed the Init() method in the base class, which would cause difficult-to-detect bugs when the base class Init() is called by mistake.

Is there any way to implement this pattern that doesn't have the pitfalls of the aforementioned approaches?

1条回答
虎瘦雄心在
2楼-- · 2019-04-25 11:58

TypeScript complains about methods not being interchangeable: what would happen if you do the following?

let a:A = new A(); // a is of type A
a.Init(1)
a = new B(); // a is still of type A, even if it contains B inside
a.Init(1) // second parameter is missing for B, but totally valid for A, will it explode?

If you don't need them to be interchangeable, modify B's signature to comply with A's:

class B extends A {
    Init(param1: number, param2?: string) { // param 2 is optional
        // some more code
    }
}

However, you might find yourself in a situation where you need to create a class with totally different method signature:

class C extends A {
    Init(param1: string) { // param 1 is now string instead of number
        // some more code
    }
}

In this case, add a list of method signatures that satisfy both current class and base class calls.

class C extends A {
    Init(param1: number)
    Init(param1: string)
    Init(param1: number | string) { // param 1 is now of type number | string (you can also use <any>)
        if (typeof param1 === "string") { // param 1 is now guaranteed to be string
            // some more code
        }
    }
}

That way the A class doesn't have to know about any of the derived classes. As a trade-off, you need to specify a list of signatures that satisfies both base class and sub class method calls.

查看更多
登录 后发表回答