DEV Community

loading...
Cover image for Typescript classes, methods, functions and other gotchas

Typescript classes, methods, functions and other gotchas

Juan F Gonzalez
Systems engineer. Frontend dev. Professional lifelong learner. Doing my best at sharing practical knowledge.
・4 min read

Hi friends!

This post is a bit different from the others I've made in that I'm not going to be talking about practical ways/ tips & tricks on how to become a better developer or tech person.

The idea for this post was sparked as I was working on a demo project at work and I was fiddling around with the online Typescript compiler.

There are some interesting things I discovered but I think I might may be missing something so I thought about sharing it with the community and be a way like @swyx says to 'learn in public'.

So here it goes!

Jumping in

The scenario

In Typescript there's the class construct that contains properties, methods and a constructor. So we could do something like this:

class Person {
    name: string;
    age: number;

    constructor(personName: string, personAge: number) {
        this.name = personName;
        this.age = personAge;
    }
}
Enter fullscreen mode Exit fullscreen mode

And compiled to ES2015 it will give me the following.

class Person {
    constructor(personName, personAge) {
        this.name = personName;
        this.age = personAge;
    }
}
Enter fullscreen mode Exit fullscreen mode

So that works and now if I want to add a method to the class to go along with those properties I'd do this:

class Person {
    name: string;
    age: number;

    constructor(personName: string, personAge: number) {
        this.name = personName;
        this.age = personAge;
    }

    greet(): string {
        return `Hello! my name is ${this.name}`;
    }
}
Enter fullscreen mode Exit fullscreen mode

And then the compiler will give me the following:

class Person {
    constructor(personName, personAge) {
        this.name = personName;
        this.age = personAge;
    }
    greet() {
        return "Hello! My name is " + this.name;
    }
}
Enter fullscreen mode Exit fullscreen mode

Now all that works, inside the constructor I can refer to the properties of the class using the this keyword, like this.name.
And with that constructor in place I can now instantiate the class and create a 'Person' object like this:

const person1 = new Person("Max", 25);
Enter fullscreen mode Exit fullscreen mode

And now this is awfully reminiscent to Java...
Anyway, after that I can now access the greet function like this:

console.log(person1.greet());
Enter fullscreen mode Exit fullscreen mode

And that will print to the console "Hello! my name is Max".

But now is when it gets very interesting. We established that besides the constructor, all a class has are properties and methods.

The properties are Typescript types and the methods are functions. Like in the constructor, we access the class properties using the this inside the methods like in the greet method when accessing the person's name.

But then I thought that it looks very plain. The function doesn't receive any parameters and doesn't do more than just returning a string with the name property.

What if I change it to a more succinct way? By the way, I'm aware of the fact that traditional functions create their own internal scope and handle the this in an particular way as opposed to arrow functions which inherit the this from the parent.

But if I have the functions already inside a class then I'm referring to the properties of the class as a whole, so no weird this behaviors in this case.

Knowing that, I can go about rewriting the greet function like this:

this.greet = () => `Hello! my name is ${this.name}`;
Enter fullscreen mode Exit fullscreen mode

That still works when logging the person's name. Except for the fact that now the Person class gets changed by the compiler into this:

class Person {
    constructor(personName, personAge) {
        this.name = personName;
        this.age = personAge;
        this.greet = () => "Hello! My name is " + this.name;
    }
}
Enter fullscreen mode Exit fullscreen mode

Ok. Now that's something different. Not only logging the result of the greet function is the same, but I can even use the export keyword and call that method from a different class defined in a different file like this:

class Employee {
    empInfo: Person;
    role: string;

    constructor(role: string) {
        this.role = role;
    }

    fullGreeting(): string {
        return `${this.empInfo.greet()} and my role is ${this.role}`;
    }
}
Enter fullscreen mode Exit fullscreen mode

And this will still work just fine.

So the behavior is the same but the fundamental change is that the function greet went from being a method of the class to become a property itself.

Also the Intellisense in VSCode would tell me just that, it is a property of the class that gives me back a string value.

I actually converted all the functions of a service class for a small project I was working on into these kind of properties and kept on building the thing like that.

Ultimately, I changed it to the originally version 'cause I realized that this way might not be the most intuitive for others when looking at the code. And we know that it is better to write readable code instead of being all 'smarty pants' with the stuff that we write.

Still, this interesting finding kept doing rounds in my head and I thought is that all?

Could there be more to this that I'm not aware of and I might be discounting just because 'it works'?

That's the whole deal right here. Typescript with some quirks of its own as well. I'm sure this would get even more quirky if it would be converted into ES5 or lower hahaha fun stuff to discover.


That's it for this post. Any comments or feedback on this is greatly appreciated.

Thanks for reading so far and I hope to see you (or read you) in the next one.

Discussion (0)

Forem Open with the Forem app