4

i tried to create a class with two constructors and find out that TypeScript are not allowing that, but it allow overloading of constructor, well I tried it to and get an error :

Build: Overload signature is not compatible with function implementation.

My code:

interface IShoppingListItem {
    name: string;
    amount: number;
}

export class ShoppingListItem implements IShoppingListItem{
    name: string;
    amount: number;

    constructor(item: IShoppingListItem);
    constructor(name: string, amount: number) {
        this.name = name;
        this.amount = amount;
    }

    copy() {
        //return new this.constructor(this);
    }
}

I have two question, first one, why can't I overloading the constructor, I guess I'm doing something wrong.

But my second question, and more interacting is, i know i constructor that get optional values. Can I, (not with the code inside the method!), create a condition on my constructor that can verify that one of the two given value have to exist, while in the signature boot are optional, like so:

constructor(item?: IShoppingListItem, name?: string, amount?: number) { 
//make a condition that item or name and amount must exist
    this.name = name;
    this.amount = amount;
}

Thanks.

2
  • 1
    Doesn't seem to be related to Angular2 at all. Typescipt != Angular2 Commented Feb 22, 2016 at 10:54
  • Opps, done it automatic without thinking, I change it now, sorry. Commented Feb 22, 2016 at 10:55

2 Answers 2

9

In Typescript, function overloading is nothing more than defining additional call signatures for a single function body. And since there is only one function body, all overload signatures must be compatible with that initial function declaration. Type- and value-checking of arguments are to be done manually.

In your case, it's a bit tricky, as interface definitions are lost upon compilation, therefore you'll have no clean approach to check if the passed argument is an implementation of the given interface. Luckily, it's an "object or string" check for your first parameter, and an "exists or not" for the second, so checking whether or not the interface is implemented is unnecessary:

export class ShoppingListItem implements IShoppingListItem {
    // 2 overload signatures from which you can choose on the invocation side
    constructor(item: IShoppingListItem);
    constructor(name: string, amount: number);

    // the function declaration itself, compatible with both of the above overloads
    // (in VS, IntelliSense will not offer this version for autocompletion)
    constructor(nameOrItem: string | IShoppingListItem, amount?: number) {
        if (typeof nameOrItem === "object") {
            // the first argument is an object, due to overload signature,
            // it is safe to assume it is of type IShoppingListItem

            // ...
        } else if (typeof nameOrItem === "string" && typeof amount === "number") {
            this.name = nameOrItem;
            this.amount = amount;
        }
    }
}

Here, both overloads are compatible with the initial signature. Parameter names do not matter, only their respective types and ordering (as well as optionality).

The TypeScript compiler is aware of typeof and instanceof checks, which results in your variable being treated as the correct type inside a conditional block as such. This is called a type guard.

Sign up to request clarification or add additional context in comments.

6 Comments

Should first two constructor() lines be in the interface?
@Sasxa AFAIK, interfaces cannot define constructors signatures in TypeScript. IMO, they shouldn't anyways, as interfaces are primarily to define usage, not creation. For the latter, it's the factory pattern to be used, I'd say.
@John White Thank you, you need to change the name to nameOrItem, after the else. I tried to to (typeof nameOrItem === "IShoppingListItem"), but it doesn't work... do you know why?
@NirSchwartz You are right, that's a mistake on my end. typeof nameOrItem does not work, because... well, see the typeof documentation, it would be a lengthy explanation for a comment. Also, interfaces do not exist at runtime, only at design- and compile time. If you need to determine between two interfaces, you need to resort to duck-typing. That is, you need to manually check if the applicable properties and methods exist on the object.
@John White And I can't do typeof nameOrItem ShoppingListItem ? a class?
|
3

You can get functionality you need with static method:

export class ShoppingListItem implements IShoppingListItem {
    name: string;
    amount: number;

    constructor(item: IShoppingListItem) {
        this.name = item.name;
        this.amount = item.amount;
    }

    static create(name: string, amount: number) {
        let item: IShoppingListItem = { name, amount };
        return new ShoppingListItem(item);
    }
}

You can create ShoppingListItem like this:

  • ShoppingListItem.create("Cat", 3)

  • ShoppingListItem({ name: "Cat", amount: 3 })

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.