Telerik blogs

Let’s look at basic TypeScript language features and how we can use them to enhance our Angular apps.

Angular is a framework that lets us create interactive web frontends for users.

It is based on the TypeScript programming language. TypeScript is a superset of JavaScript and it supports all language features included with JavaScript.

TypeScript mainly adds data type checking capabilities that are missing in JavaScript. This makes programming easier since we don’t have to check the data type of variables and other values ourselves.

We can use all TypeScript features in our Angular apps since the Angular CLI comes with the TypeScript compiler built in.

In this article, we will look at basic TypeScript language features and how we can use them in our Angular apps.

Basic TypeScript Language Features

TypeScript comes with some common language features that we use to write programs.

Variable Data Types

One of the basic TypeScript features that we use regularly in our apps is variable declarations.

In JavaScript, we can define variables but we can’t add type annotation to them. But we can add data type annotations to variables with TypeScript.

To declare variables with data type annotations with TypeScript, we write something like:

const a: number = 1;
const b: string = "foo";

We declare a variable a which is a number, and variable b which is a string. We can assign numbers to number variables. And we can assign strings to string variables.

If we assign something that doesn’t match the type we specify, we’ll get errors from the TypeScript compiler.

Function Data Types

We can add data type annotations to parameters and return values of functions with TypeScript.

For instance, we write:

const add = (a: number, b: number): number => a + b;

to define the add function.

It takes parameters a and b which are both numbers.

And we specify that the type of the return value of the function is number after the colon that comes after the function signature. Therefore, we know a + b is a number.

If we call add with anything other than two numbers, we will get a type error from the TypeScript compiler. This lets us avoid a lot of mistakes with data types when programming.

Object Data Types

In addition to defining data types for primitive values, we can define data type annotations for objects. There are two ways to define types for objects. One is by defining interfaces and the other is defining a type alias.

To define an interface, we use the interface keyword.

For instance, we write:

interface Person {
  firstName: string;
  lastName: string;
}

to define the Person interface that has the firstName and lastName string properties.

Then we can use it as the type for a variable by writing:

interface Person {
  firstName: string;
  lastName: string;
}

const person: Person = { firstName: "jane", lastName: "smith" };

to define the person variable of type Person. We set it to an object with the firstName and lastName properties to match the type.

The object having the same property names and the same types for the properties will make the object match the type.

We can define a type alias with the type keyword. For instance, we write:

type Person = {
  firstName: string;
  lastName: string;
};

const person: Person = { firstName: "jane", lastName: "smith" };

to define the Person type. And we set the person variable to the same value to match the type specification.

The difference between an interface and a type is that an interface can inherit from other interfaces.

For instance, we write:

interface Animal {
  name: string;
}

interface Dog extends Animal {
  breed: string;
}

const dog: Dog = { name: "jane", breed: "poodle" };

to define the Animal interface and the Dog interface that inherits from the Animal interface. And we define the dog variable of type Dog that has both properties. This is because Dog inherits properties from Animal.

We can inherit from interfaces but not types. Also, we can make properties optional in an interface or type.

To make the age property optional, we put a question mark after the property name in the Person interface:

interface Person {
  firstName: string;
  lastName: string;
  age?: number;
}

We can also add dynamic properties into interfaces and types with index signatures.

For instance, we write:

interface Dynamic {
  [key: string]: any;
}

const obj: Dynamic = { foo: 1, bar: 2 };

to define the Dynamic interface that takes any string property keys and we can set them to any type.

As a result, we can set obj to an object with any property with string keys since it has the Dynamic type.

Union and Intersection Types

We can combine multiple types into one. One way is to define an union of two types. And the other is to define an intersection of two types.

Variables of a union type can be assigned to anything that matches one of the types listed or a combination of them.

Variables of an intersection type need to have values that match all the types listed combined.

For instance, we write:

type Foo = {
  foo: string;
};

type Bar = {
  bar: number;
};

type Baz = Foo | Bar;
const x: Baz = { foo: "foo" };
const y: Baz = { bar: 1 };
const z: Baz = { foo: "foo", bar: 1 };

to define the Foo and Bar type aliases and assign the union of them to Baz.

We define the x, y and z variables with type Baz that’s assigned to an object that has the foo or bar properties or both properties together. And they’re all valid since they have at least one of the properties listed.

We can also have unions of primitive types.

For instance, we write:

type NumberOrString = number | string;
const x: NumberOrString = 1;
const y: NumberOrString = "foo";

to define the NumberOrString type which is a union of number or string. And we can assign variables with the type to a number or a string.

We can define intersection types with &. For instance, we write:

type Foo = {
  foo: string;
};

type Bar = {
  bar: number;
};

type Baz = Foo & Bar;
const x: Baz = { foo: "foo", bar: 1 };

to define the Baz type which is an intersection of the Foo and Bar types.

Therefore, variables of type Baz need to have both the foo and bar properties since intersection types combine both types into one.

Class Decorators

In TypeScript, class decorators is a function that manipulates constructor functions.

For instance, we write:

const sealed = (constructor: Function) => {
  Object.seal(constructor);
  Object.seal(constructor.prototype);
};

@sealed
class C {
  name: string;

  constructor(t: string) {
    this.name = t;
  }
}

to define the sealed constructor that takes the constructor function.

And we call Object.seal with constructor and constructor.prototype to stop us from modifying the constructor and its prototype after it’s defined.

We put the decorator right above the class C to modify class C with the operations in the sealed constructor.

Classes are just constructors with a convenient syntax in JavaScript and TypeScript, so they’re the same.

Decorators are used for many classes in Angular including components.

Enums

Enums is a data type that can take a set of predefined values. TypeScript has enums built in as a language feature. We can define one with the enum keyword.

For instance, we write:

enum Fruit {
  APPLE,
  ORANGE,
  BANANA,
}

const fruit: Fruit = Fruit.APPLE;

to define the Fruit enum with a few choices.

Then we can use the enum name as the type and set the variable to one of the enum values.

Using TypeScript Features in Angular

We can use all the TypeScript language features with Angular. The ng generate command, or ng g command for short, lets us create various TypeScript entities automatically.

We can use it to create a class, enum or interface files.

For instance, we run:

ng g class MyClass
ng g interface MyInterface
ng g enum MyEnum

to create class, interface and enum files automatically.

The file will have one class, interface or enum that’s exported with export.

For instance, we run:

ng g interface Person

to create the src/app/person.ts file.

It it, we change it to:

export interface Person {
  firstName: string;
  lastName: string;
}

to add the firstName and lastName fields to it.

Then in app.component.ts, we write:

import { Component } from "@angular/core";
import { Person } from "./person";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
})
export class AppComponent {
  person: Person = {
    firstName: "jane",
    lastName: "smith",
  };

  get fullName() {
    const { person } = this;
    return `${person.firstName} ${person.lastName}`;
  }
}

to import the Person interface we just created and set that as the type for the person instance variable in the AppComponent class.

We also add a getter to return the value of the firstName and lastName combined as fullName.

In app.component.html, we write:

<p>{{ person.firstName }}</p>
<p>{{ person.lastName }}</p>
<p>{{ fullName }}</p>

to render the values.

As a result, we see:

jane
smith
jane smith

on the screen.

AppComponent is made a component with the @Component decorator.

It changes the AppComponent class to set the HTML selector, template URL and stylesheet URLs.

Also, we set the person instance variable’s data type to Person so we can only enter the properties listed in the Person interface without errors.

We then render the instance variables in the app.component.html template since we set templateUrl to app.component.html as the template path for AppComponent when we call the Component function to return a decorator that modifies AppComponent to be a component.

Conclusion

Angular is based on the TypeScript programming language, a superset of JavaScript that supports all language features included with JavaScript.

TypeScript enhances JavaScript by providing data type checking features that aren’t available with JavaScript. It lets us write code without having to look up the data type for each variable since code editors can provide autocomplete hints by the type definitions.

Having TypeScript type definitions also make code editors aware of the members that are available in objects, classes and modules. It also lets us prevent runtime errors caused by mismatched data types between variables and values when we are doing any operation.

The Angular CLI provides us with commands to create some TypeScript entities with one command.

And we can use any TypeScript language feature in Angular projects created with Angular CLI since it comes with the TypeScript compiler.


About the Author

John Au-Yeung

John Au-Yeung is a frontend developer with 6+ years of experience. He is an avid blogger (visit his site at https://thewebdev.info/) and the author of Vue.js 3 By Example.

Related Posts

Comments

Comments are disabled in preview mode.