Typescript Abstract Property

In this blog post, we examine how class definitions work in TypeScript:

TypeScript 3.0.3 ui-button ui-button Class Implementing Interfaces Select All Download.

  • Use inheritance, abstract classes, and polymorphism in TypeScript. Object oriented programming languages such as C# allow you to inherit functionality from base class. TypeScript also offers inheritance capabilities so that your classes can inherit from other classes. TypeScript doesn't support multiple inheritance (although your class can.
  • Typescript supports object-oriented programming concepts using classes, interfaces, and abstract classes. The class is defined with the abstract keyword is called abstract classes. It is used to provide an abstraction These may not be created instances, instead of instances created by extending abstract classes.
  • Abstract Factory in TypeScript Abstract Factory is a creational design pattern, which solves the problem of creating entire product families without specifying their concrete classes. Abstract Factory defines an interface for creating all distinct products but leaves the actual product creation to.
  • First, we take a quick look at the features of class definitions in plain JavaScript.
  • Then we explore what additions TypeScript brings to the table.

Table of contents:

Cheat sheet: classes in plain JavaScript #

This section is a cheat sheet for class definitions in plain JavaScript.

Basic members of classes #

Modifier: static#

Modifier: # (private) #

Warning:

Typescript Abstract Property For Sale

Modifiers for accessors: get (getter) and set (setter) #

There are two kinds of accessors: getters and setters.

Modifier for methods: * (generator) #

Modifier for methods: async#

Computed class member names #

Comments:

  • The main use case for this feature is symbols such as Symbol.iterator. But any expression can be used inside the square brackets.
  • We can compute the names of fields, methods, and accessors.
  • We cannot compute the names of private members (which are always fixed).

Combinations of modifiers #

Fields:

Methods:

levelaccessorasyncgeneratorvisibility
(prototype)
(prototype)get
(prototype)set
(prototype)async
(prototype)*
(prototype)async*
(prototype-associated)#
(prototype-associated)get#
(prototype-associated)set#
(prototype-associated)async#
(prototype-associated)*#
(prototype-associated)async*#
static
staticget
staticset
staticasync
static*
staticasync*
static#
staticget#
staticset#
staticasync#
static*#
staticasync*#

Limitations of methods:

  • Accessors can’t be async or generators.

Under the hood #

It’s important to keep in mind that with classes, there are two chains of prototype objects:

  • The instance chain which starts with an instance.
  • The static chain which starts with the class of that instance.

Consider the following JavaScript (not TypeScript!) example:

The two prototype chains look as follows:

More information #

  • Public fields, private fields, private methods/getters/setters (blog post)
  • All remaining JavaScript class features (chapter in “JavaScript for impatient programming”)

Non-public data slots in TypeScript #

By default, all data slots in TypeScript are public properties. There are two ways of keeping data private:

  • Private properties
  • Private fields

We’ll look at both next.

Note that TypeScript does not currently support private methods.

Private properties #

Any property can be made private by prefixing it with the keyword private (line A):

We now get compile-time errors if we access that property in the wrong scope (line A):

However, private doesn’t change anything at runtime. There, the property .name is indistinguishable from a public property:

We can also see that private properties aren’t protected at runtime when we look at the JavaScript code that the class is compiled to:

Private fields #

Since version 3.8, TypeScript also supports private fields:

That code is mostly used the same way as the other version:

However, this time, the data is completely safe. Using the private field syntax outside classes is even a JavaScript syntax error (which is why we have to use eval() in line A, so that the code runs):

The compilation result is much more complicated now:

This code uses a common technique for keeping instance data private:

  • Each WeakMap implements one private field.
  • It associates instances with private data.

For more information on this technique, see “JavaScript for impatient programmers”.

Private properties vs. private fields #

  • Downsides of private properties:
    • We can’t reuse the names of private properties in subclasses (because the properties aren’t private at runtime).
    • No protection at runtime.
  • Upside of private properties:
    • Clients can circumvent the protection and access private properties. This can be useful if someone needs to work around a bug. In other words: Being completely protected has pros and cons.

Protected properties #

Private properties can’t be accessed in subclasses (line A):

We can fix the previous example by switching from private to protected in line A (we also switch in line B, for consistency’s sake):

Private constructors #

We can also make constructors private. That is useful when we have static factory methods and want clients to always use those methods, never the constructor directly. Static methods can access private instance members, which is why the factory methods can still use the constructor.

In the following code, there is one static factory method DataContainer.create(). It sets up instances via asynchronously loaded data. Keeping the asynchronous code in the factory method enables the actual class to be completely synchronous:

In real-world code, we would use fetch() or a similar Promise-based API to load data asynchronously in line A.

Right now, the private constructor prevents DataContainer from being subclassed. If we want to allow subclasses, we can use protected.

Initializing instance properties #

Strict property initialization #

If the compiler setting --strictPropertyInitialization is switched on (which is the case if we use --strict), then TypeScript checks if all declared instance properties are correctly initialized:

  • Either via assignments in the constructor:

  • Or via initializers for the property declarations:

However, sometimes we initialize properties in a manner that TypeScript doesn’t recognize. Then we can use exclamation marks (definite assignment assertions) to switch off TypeScript’s warnings (line A and line B):

Example: setting up instance properties via objects #

In the following example, we also need definite assignment assertions. Here, we set up instance properties via the constructor parameter props:

Notes:

  • In line B, we initialize all properties: We use Object.assign() to copy the properties of parameter props into this.
  • In line A, the implements ensures that the class declares all properties that are part of interface CompilerErrorProps.

Making constructor parameters public, private, or protected#

If we use the keyword public for a constructor parameter, then TypeScript does two things for us:

  • It declares a public instance property with the same name.
  • It assigns the parameter to that instance property.

Therefore, the following two classes are equivalent:

If we use private or protected instead of public, then the corresponding instance properties are private or protected (not public).

Property

Abstract classes #

Two constructs can be abstract in TypeScript:

  • An abstract class can’t be instantiated. Only its subclasses can – if they are not abstract, themselves.
  • An abstract method has no implementation, only a type signature. Each concrete subclass must have a concrete method with the same name and type signature as that abstract method.
    • If a class has any abstract methods, it must be abstract, too.

The following code demonstrates abstract classes and methods.

On one hand, there is the abstract superclass Printable and its helper class StringBuilder:

Typescript Implement Abstract Class

On the other hand, there are the concrete subclasses Entries and Entry:

And finally, this is us using Entries and Entry:

Notes about abstract classes:

  • They can be seen as interfaces with bundled implementations.
  • However, a class can only extend one abstract superclass but implement multiple interfaces.
  • “Abstractness” only exists at compile time. At runtime, abstract classes are normal classes and abstract methods don’t exist (due to them only providing compile-time information).
  • Abstract classes can be seen as templates where each abstract method is a blank that has to be filled in (implemented) by subclasses.

Summary: in this tutorial, you will learn about TypeScript abstract classes.

Introduction to TypeScript abstract classes

An abstract class is typically used to define common behaviors for derived classes to extend. Unlike a regular class, an abstract class cannot be instantiated directly.

To declare an abstract class, you use the abstract keyword:

Typescript abstract static

Typically, an abstract class contains one or more abstract methods.

Typescript abstract readonly property

An abstract method does not contain implementation. It only defines the signature of the method without including the method body. An abstract method must be implemented in the derived class.

The following shows the Employee abstract class that has the getSalary() abstract method:

In the Employee class:

  • The constructor declares the firstName and lastName properties.
  • The getSalary() method is an abstract method. The derived class will implement the logic based on the type of employee.
  • The getFullName() and compensationStatement() methods contain detailed implementation. Note that the compensationStatement() method calls the getSalary() method.

Because the Employee class is abstract, you cannot create a new object from it. The following statement causes an error:

Error:

Typescript Abstract Property Undefined

The following FullTimeEmployee class inherits from the Employee class:

In this FullTimeEmployee class, the salary is set in the constructor. Because the getSalary() is an abstract method of the Employee class, the FullTimeEmployee class needs to implement this method. In this example, it just returns the salary without any calculation.

Typescript Abstract Readonly Property

The following shows the Contractor class that also inherits from the Employee class:

In the Contractor class, the constructor initializes the rate and hours. The getSalary() method calculates the salary by multiplying the rate with the hours.

The following first creates a FullTimeEmployee object and a Contractor object and then shows the compensation statements to the console:

Output:

It’s a good practice to use abstract classes when you want to share code among some related classes.

Typescript Abstract Class Property

Summary

Typescript Abstract Property Default Value

  • Abstract classes cannot be instantiated.
  • An Abstract class has at least one abstract method.
  • To use an abstract class, you need to inherit it and provide the implementation for the abstract methods.