Introduction
We use TypeScript to add type checking to our code. In this lesson we will looking at how we can add types to our code and the different type examples.
Adding a type
In most cases, we typically add a data type with a colon :
and then the data type afterwards.
Let’s have another look at the previous example we worked with.
function addNumbers(a: number, b: number): number {
return a + b;
}
console.log(addNumbers(10, 20));
Our first parameter, a
, can be seen to have the number
type added to it.
a: number;
TypeScript will now check that the parameter a
is always a number. If we tried to pass a string as a
then we would see that there is a TypeScript error: Argument of type 'string' is not assignable to parameter of type 'number'.ts(2345)
Above: A type error being shown because the incorrect type was passed into the function.
Primitive types
The most commonly used primitives are number
, string
and boolean
. You can use these as written for your types e.g.
function exampleFunction(myNumber: number, myString: string, myBoolean: boolean) {
/* Code here */
}
The any
type
TypeScript has an any
type which means that a value can be of any type.
We do not want to use the any
type because it defeats the point of TypeScript. You should not be using the any
type for applications.
With the above being said, it is fine to use the any
type as a placeholder while you are still writing out your code, with the intention of correctly typing the code once you know what the type is.
Adding type annotations to variables and type inference
When we create a variable we can optionally add types to our variables.
In the example below we add a string to the name of our user:
let firstName: string = 'Ola';
Type inference
Type inference is a fancy way of saying that TypeScript will automatically figure out the type of a variable based on the contents of that variable.
In the example above we specified a string
type and then set the value to Ola
.
TypeScript would have actually automatically typed firstName
as a string because we initially set it to a string. This is type inference.
If we remove the string
type and hover over the variable name, we will see that our type is set to string
.
Above: Seeing our type is set to string through type inference.
Arrays
We can type arrays by adding the type and then square brackets []
e.g. number[]
.
You could do this with any type e.g. string[]
, boolean[]
.
You can also type arrays by writing the Array
keyword and then the type in angled brackets <>
e.g. Array<number>
.
Functions
There are two main aspects to typing a function.
Adding types to the:
- Parameters
- The return type
1. Typing the parameters
We should add type annotations to our parameters being passed to a function.
function addNumbers(a: number, b: number) {
return a + b;
}
2. Adding the return type
We should also add a return type to our functions. We add the return type with a colon :
immediately after the parameters. In the example below, we are adding a string return type:
function addNumbers(a: number, b: number): string {
return `The sum of the numbers is ${a + b}`;
}
Objects
We can add types to the properties of our objects. In the example below, we add types to an object we are passing in.
function greetPerson(person: { firstName: string; lastName: string }) {
return `Hello ${person.firstName} ${person.lastName}.`;
}
greetPerson({ firstName: 'Ola', lastName: 'Nordmann' });
NOTE: We typically use Type aliases and interfaces when dealing with objects which we will be looking at shortly.
Unions
We might want to combine types. For example, we might have an age
variable where want the value to either be a string or a number. We can use unions to do this. A union is created through the use of separating types with the pipe |
character.
In the example below we are creating a userAge
variable which has either a number
or a string
type.
let userAge: number | string;
userAge = 10; // No error
userAge = '10'; // No error
userAge = true; // Error
Adding types reminds us to add the right safeguards to deal with different parameter types. In the example below we are parsing a
so that we ensure we have mathematical addition instead of string concatenation.
function addNumbers(a: string | number, b: number): number {
if (typeof a === 'string') {
return parseFloat(a) + b;
} else {
return a + b;
}
}
Type aliases
A type alias allows us to specify a shape shape which we can then reuse in our code.
In the example below we create a Person
type alias and then use that in a function.
type Person = {
firstName: string;
lastName: string;
isAdmin: boolean;
age: number;
};
function greetPerson(person: Person) {
return `Hello ${person.firstName} ${person.lastName}!`;
}
You will see that when we use the person
parameter we get auto-completion, one of the great benefits of TypeScript:
Above: Type auto-completion.
Interfaces
Interfaces are similar to type aliases.
Below is the same code as the type alias however we have switched out the type alias for an interface.
interface Person {
firstName: string;
lastName: string;
isAdmin: boolean;
age: number;
}
function greetPerson(person: Person) {
return `Hello ${person.firstName} ${person.lastName}!`;
}
Differences between types aliases and interfaces
Type aliases and interfaces are the same for the most part.
The differences are:
1. Extending a type alias/interface
We can extend (grow) our type aliases and interfaces however they are done slightly differently.
In the example below we extend a type alias using an intersection.
type Person = {
firstName: string;
lastName: string;
};
type Student = Person & {
marks: number[];
};
const teacher: Student = {
firstName: 'Ola',
lastName: 'Nordmann',
marks: [95, 97, 85]
};
In the example below, we extend the interface using the extends keyword:
interface Person {
firstName: string;
lastName: string;
}
interface Student extends Person {
marks: number[];
}
const teacher: Student = {
firstName: 'Ola',
lastName: 'Nordmann',
marks: [95, 97, 85]
};
2. Changing a type alias/interface
We can’t change a type alias once it has been created.
The code below will display an error:
type Person = {
firstName: string;
lastName: string;
};
type Person = {
title: string;
};
For interfaces, we can change the interface without an error being shown:
interface Person {
firstName: string;
lastName: string;
}
interface Person {
title: string;
}
Optional types
We can create optional types by using a question ?
mark. Adding an optional type means that the value does not have to be provided.
type Person = {
firstName: string;
lastName: string;
isAdmin: boolean;
age: number;
myOptionalValue?: number;
};
In the example above, we have added an extra property myOptionalValue
and have made this optional. This means we will not have to pass through myOptionalValue
when using the Person
type.
Type assertions
Type assertions are for when we have values that TypeScript would not be able to infer.
An example of this is when we are getting an HTML element, such as an <input>
element. TypeScript would not be able to infer that the element we are getting is an input as it is unaware of the HTML DOM. We can use a type assertion to let TypeScript know that the element is expected to be an <input>
, which then leads to code suggestions based on an input element.
const nameInput = document.getElementById('name-input') as HTMLInputElement;
Above: Type Assertion leading to auto-completion of code.
We can add type assertions in 2 ways:
Adding your type in angled brackets next to the variable name:
Using the
as
keyword.
Consider the following example where we look at the two types of type assertions:
let value: any = 10;
// Example 1: angled brackets
let formattedPrice1 = <number>value.toFixed(2);
// Example 2: "as" keyword
let formattedPrice2 = (value as number).toFixed(2);
Literal types
Literal types allow us to specify exact values in our types. We can use unions in our literal types to ensure that only specific values are being used.
In the example below we are creating a firstName
variable that only accepts Ola
and Kari
as values. Trying to set firstName
to John
will display an error:
let firstName: 'Ola' | 'Kari' = 'Ola';
firstName = 'Ola';
firstName = 'Kari';
firstName = 'John';
Above: The wrong name being used in our literal type shows an error.
Enums
Enums allow us to specify a set of values to a type so that only these values can be selected.
In the example below we have created a Fruit
enum with a list of fruits. Only the values we have listed can be selected:
enum Fruit {
Apple,
Banana,
Orange
}
Above: An enum example showing us code suggestions.
Something to note is that the enums will evaluate to indexes, starting with 0 i.e.
enum Fruit {
Apple, // Value is 0
Banana, // Value is 1
Orange // Value is 2
}
We could instead specify strings for our enums:
enum Fruit {
Apple = 'APPLE', // Value is "APPLE"
Banana = 'BANANA', // Value is "BANANA"
Orange = 'ORANGE' // Value is "ORANGE"
}
When we use these enums we will now instead have the strings being the result.
NOTE: Enums are not natively present in JavaScript and are added by TypeScript, as seen in the image below where an enum is converted to an object after compilation. Using enums over string literals might not be preferred by some workplaces which is important to keep in mind.
Lesson task
Goal
For the student to demonstrate they can use TypeScript types.
Brief
Follow the Level 1 Process as instructed.
NOTE: Lesson tasks do not get submitted on Moodle and are not assessed by tutors. They are mainly there for you to practise what you have learned in the lesson.
Level 1 process
- Add an interface, called
Game
orIGame
, that contains the correct types based on the information given:
id: number
title: string
isMultiplayer: boolean
yearReleased: number
numberOfPlayers: number, optional
genre: string
- Create an enum called
Genre
that has the following fields game fields:
Adventure
Action
RPG
Sports
Replace the genre
field in your interface width the enum you have created.
- Create a
createGame
function that returns an object that matches the shape of the interface you have created. The return type must be the interface you have created. Make sure to pass in the right parameters and types.