Learn TypeScript

Working with standard JavaScript classes

Working with standard JavaScript classes

In this lesson, we will learn how we can work with the standard Javascript features of classes in a strongly-typed manner in TypeScript.

Implementing fields

Let's explore a basic class by carrying out the following steps:

  • Open the TypeScript playground by clicking the link below:
Open TypeScript Playground
  • Make sure the TypeScript version in the Playground is set to at least v4 and paste the code from below:
class Product {
name;
price;
}

The code contains a basic class for a product containing fields for the name and price of the product.

🤔

What is the inferred type given to the name and price fields?

We can add type annotations to class fields using the syntax that we are now familiar with.

  • Let's add type annotations to the name and price fields so that name can be any string and the price must be numeric:
class Product {
name: string;
price: number;
}
🤔

A type error occurs in both of these fields. What is the reason for the error?

In TypeScript, if strict mode is on, fields must be given an initial value or be optional

  • Resolve the error by making the fields optional.
  • Create a table1 variable that will be an instance of our class:
const table1 = new Product();
  • Assign the following values to the class instance fields:
table1.name = "Table1";
table1.price = "$300";
🤔

Is a type error raised on any of the assignments? If so, change the assignment(s) so that no type error occurs.

Implementing methods

We can also add strongly-typed methods to classes.

  • Let's add the following copy method to our class:
class Product {
...
copy(name) {
const copiedProduct = new Product();
copiedProduct.name = name;
copiedProduct.price = this.price;
return copiedProduct
}
}

The method creates a new Product instance with the specified name with the price from the current instance.

  • The method's name parameter isn't strongly-typed yet. Add a type annotation to ensure this is a string.
  • Create a table2 variable assigned to the copy of the table1 product with the name 'Table2'.

Notice the intellisense as you enter the code.

Nice!

Implementing constructors

We are going to force the product's name and price to be specified when it is instantiated.

  • Remove the optional flag from the name and price fields and add a constructor to initalize these:
class Product {
name: string;
price: number;
constructor(name, price) {
this.name = name;
this.price = price;
}
...
}
  • The constructor parameters aren't strongly-typed yet. Add type annotations to ensure name is a string, and price is a number.
  • The copy method now has a type error because Product is instantiated without a name or price. Resolve this error.
  • Resolve a similar type error with the table1 variable assignment.

That was great how TypeScript helped us refactor our class implementation and resolve breakages in consuming code.

Property inference from constructors

  • Let's remove the type annotations on the property:
class Product {
name;
price;
...
}
🤔

What has TypeScript inferred the types on these properties to be?

Wow, TypeScript has cleverly inferred the types of the properties from the assignments in the constructor!

This was added in TypeScript 4.0. In previous versions the types will inferred to be any.

Implementing static classes

We will enhance our Product to include a static method to check whether products are equal.

  • Add the following static equal method to our class:
class Product {
...
static equal(product1, product2) {
return product1.name === product2.name && product1.price === product2.price;
}
}
  • The method isn't strongly-typed yet. Add type annotations to the parameters to make it strongly-typed.
  • Use the static method to check whether our table1 and table2 products are equal.

Classes v type aliases v interfaces

Classes create object structures like type aliases and interfaces do. A key difference is that a class contains method implementation, whereas type aliases and interfaces don't. Classes can also be instantiated and execute logic during this process. Type aliases and interfaces can't be instantiated because they only contain structural information.

So, a class is useful for representing an object blueprint that contains constructor and method implementation.

Summary

Classes can be made strongly-typed by adding type annotations to constructor parameters, fields, and method parameters. Alternatively, the type for constructor parameters, fields, and method parameters can be inferred if they are assigned to values in their declaration.

Fields and parameters can be made optional using a question mark (?) before their type annotation.

In the next lesson, we will learn how we can control the access that consumers have to class members.

© 2023 Carl Rippon
Privacy Policy
This site uses cookies. Click here to find out more