90

Let's say I have a class Person which looks like this:

class Person {
    constructor(
        public firstName: string,
        public lastName: string,
        public age: number
    ) {}
}

I have overridden the toString method as follows.

public toString(): string {
    return this.firstName + ' ' + this.lastName;
}

Now I expect to be able to do the following, which works in runtime:

function alertMessage(message: string) {
    alert(message);
}

alertMessage(new Person('John', 'Smith', 20));

But this still gives me this error:

TS2345: Argument of type 'Person' is not assignable to parameter of type 'string'.

How can I assign a Person to this string argument?

Duncan Lukkenaer
  • 12,050
  • 13
  • 64
  • 97
  • 8
    Have you tried your own example? seems to work already. https://jsfiddle.net/sy8wttvw/ – Kruga Feb 12 '16 at 11:36

2 Answers2

83

Overriding toString works kind of as expected:

class Foo {
    private id: number = 23423;
    public toString = () : string => {
        return `Foo (id: ${this.id})`;
    }
}

class Bar extends Foo {
   private name:string = "Some name"; 
   public toString = () : string => {
        return `Bar (${this.name})`;
    }
}

let a: Foo = new Foo();
// Calling log like this will not automatically invoke toString
console.log(a); // outputs: Foo { id: 23423, toString: [Function] }

// To string will be called when concatenating strings
console.log("" + a); // outputs: Foo (id: 23423)
console.log(`${a}`); // outputs: Foo (id: 23423)

// and for overridden toString in subclass..
let b: Bar = new Bar();
console.log(b); // outputs: Bar { id: 23423, toString: [Function], name: 'Some name' }
console.log("" + b); // outputs: Bar (Some name)
console.log(`${b}`); // outputs: Bar (Some name)

// This also works as expected; toString is run on Bar instance. 
let c: Foo = new Bar();
console.log(c); // outputs: Bar { id: 23423, toString: [Function], name: 'Some name' }
console.log("" + c); // outputs: Bar (Some name)
console.log(`${c}`); // outputs: Bar (Some name)

What can sometimes be an issue though is that it is not possible to access the toString of a parent class:

console.log("" + (new Bar() as Foo));

Will run the toString on Bar, not on Foo.

lifeless
  • 4,045
  • 1
  • 17
  • 6
Nypan
  • 6,980
  • 3
  • 21
  • 28
  • That kinda works for me, but getting more specific. I have a ts class like this `code` module Entidades { export class eEpisodio{ public Id: numer} }`code` If i try to use my toString() method adding some property, it doesn't work, it doesn't seem to find the Id property (or any at all) – Mario Garcia Feb 12 '16 at 11:50
  • 1
    I updated my answer. What might have been your problem is defining toString like a function. Defining it as a lambda property might work better (like i have done in my new more exhaustive example.) – Nypan Feb 12 '16 at 12:17
  • 1
    @Nypan Is there a pro argument to use `public toString = () : string => {` instead of `public toString(): string {` ? – illnr Jun 11 '17 at 06:13
  • 2
    Yes, using `t() : =>` ensures that `this`will be what you expect in the toString override. You can read up on [arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) to learn more about this. But the basic difference here is that arrow functions does not bind their own this. – Nypan Jun 15 '17 at 10:34
15

As pointed out by @Kruga, the example actually seemed to work in runtime JavaScript. The only problem with this is that TypeScript shows a type error.

TS2345: Argument of type 'Person' is not assignable to parameter of type 'string'.

To resolve this message, you must either:

  • Call .toString() explicitly
  • Or concatenate the object with a string (e.g. `${obj}` or obj + '')
  • Or use obj as any (not recommended as you will lose type safety)
Duncan Lukkenaer
  • 12,050
  • 13
  • 64
  • 97