Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why TypeScript toString method is not invoked as desired

I have a pretty simple code.

There is a model with toString overwritten:

policy.ts:

export class Policy {
  public id: number;
  public name: string;
  public description: string;

  toString(): string {
    return this.name + ': ' + this.description;
  }
}

Then I use the Policy model in two ways.

binding to create-policy.component.html:

...
<input type="text" name="name" [(ngModel)]="policy.name" required>

and it in the component create-policy.component.ts:

import { Component, OnInit } from '@angular/core';
import { Policy } from '../models/policy';

@Component({
    selector: 'app-create-policy',
    templateUrl: './create-policy.component.html',
    styleUrls: ['./create-policy.component.css'],
})
export class CreatePolicyComponent implements OnInit {
    policy: Policy = {
        id: 0,
        name: '',
        description: '',
    };
    constructor() {}

    ngOnInit() {}
    onSubmitPostHttp() {
        console.log('Started onSubmitPostHttp');
        console.log('policy=' + this.policy.toString());
    }
}

Still on the console I do not get properties; it prints:

create-policy.component.ts:23 policy=[object Object]

Direct logging properties works properly:

 console.log("policy=" + this.policy.name);

That prints:

policy=ss

like image 850
Alex Avatar asked Oct 26 '25 13:10

Alex


2 Answers

policy isn't an instance of Policy and still uses native toString method.

The fact that Policy class is used as interface like policy: Policy doesn't instantiate it. Since plain object conforms to this interface (it already has toString method), this doesn't cause type error.

It can be:

policy = Object.assign(new Policy, {
  id: 0,
  name: "",
  description: ""
});

At this point it becomes obvious that Policy class wasn't designed for what it's used, it's better to move property initialization to its constructor:

export class Policy {
  constructor(
   public id: number,
   public name: string,
   public description: string
 ) {}

 toString(): string {
   return this.name + ': ' + this.description;
 }

}

and

policy = new Policy(
  id: 0,
  name: "",
  description: ""
);
like image 193
Estus Flask Avatar answered Oct 28 '25 02:10

Estus Flask


You are creating this.policy as a new object without overriding the toString() method. And that is why it still uses the default implementation when logging the result.

Look at the generated javascript code when creating a typescript class:

TS Code:

export class Policy {
    public id: number;
    public name: string;
    public description: string;

    toString(): string {
        return this.name + ': ' + this.description;
    }
}

let pol: Policy = {
    id: 0,
    name: "",
    description: ""
};

JS Code:

define(["require", "exports"], function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    var Policy = /** @class */ (function () {
        function Policy() {
        }
        Policy.prototype.toString = function () {
            return this.name + ': ' + this.description;
        };
        return Policy;
    }());
    exports.Policy = Policy;
    var pol = {
        id: 0,
        name: "",
        description: ""
    };
});

As you can see here, the toString() method is added to the prototype of the Policy object. There are alot you can read about prototypes in javascript but the key thing here is that the prototype for the policy does not get called unless you use the new operator.

As estus mentioned; all objects in javascript has a toString method, it is included in the object prototype. That is the reason why you are not getting any errors when creating your object directly, it has all the correct properties and methods for a policy object and that is why typescript allow this behaviour.

like image 33
hagner Avatar answered Oct 28 '25 04:10

hagner



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!