Search code examples
javascripttypescriptclasstranspiler

typescript transpilation breaking


Problem statement :

Define a variable with type number but when assign any string into this variable and try to transpile it into JavaScript. Transpiler not giving any error.

Problem Demonstration :

class.ts

class User {
    age:number;
    name:string;
    phone:number | string;

    constructor(a, n, p) {
        this.age = a;
        this.name = n;
        this.phone = p;
    }

    getDetails() {
        return "Age : " + this.age + ", Name : " + this.name + ", Phone : " + this.phone;
    }
}

var firstUser = new User("27", "Rohit", "945*****44");

console.log(firstUser.getDetails());

In above code, We defined the age variable as number but while creating the instance of the class User i am passing age as "27" which is string. Hence, while transpilation it should thrown error but it is transpiled into JavaScript without any errors.

Transpilation Output :

enter image description here

But in the same code base if I directly assign the string to this.age variable in the constructor function. Transpiler throws an error.

class.ts

class User {
    age:number;
    name:string;
    phone:number | string;

    constructor(a, n, p) {
        this.age = "27";
        this.name = n;
        this.phone = p;
    }

    getDetails() {
        return "Age : " + this.age + ", Name : " + this.name + ", Phone : " + this.phone;
    }
}

var firstUser = new User("27", "Rohit", "945*****44");

console.log(firstUser.getDetails());

Transpilation output :

enter image description here


Solution

  • This is the expected behavior. Your constructor has 3 parameters with no type annotation, so their implicit type will be any. You can assign anything to any (so you can assign a string to any) and you can assign from any (so you can assign the number field from the a parameter of type any). Basically in this case any erases the type of whatever you passed in.

    Since a should be a number we should provide an explicit annotation and then we will get an error when we pass in a string (and this is the expected behavior)

    class User {
        age:number;
        name:string;
        phone:number | string;
    
        constructor(a: number, n: string, p: number | string) {
            this.age = a;
            this.name = n;
            this.phone = p;
        }
    
        getDetails() {
            return "Age : " + this.age + ", Name : " + this.name + ", Phone : " + this.phone;
        }
    }
    
    var firstUser = new User("27", "Rohit", "945*****44"); // error
    var secondUser = new User(27, "Rohit", "945*****44"); // ok
    

    You can avoid having to specify the type twice and doing the assignment by declaring the fields as constructor arguments. The following code is equivalent to the above class:

    class User {
    
        constructor(
            public age:number, // Both a constructor argument and a field declaration, the compiler will perform the this.age = age assignemet for us
            public name:string,
            public phone:number | string) {
        }
    
        getDetails() {
            return "Age : " + this.age + ", Name : " + this.name + ", Phone : " + this.phone;
        }
    }
    

    If you want to avoid having parameters and variables implicitly of type any you can use the noImplicitAny compiler option to have an error if no type annotation is specified and the type can't be inferred.