TypeScript
- Types
- Type Inference
- Object Types
- Array Types
- Tuple Types
- Enums
- Union Types
- Type Aliasing
- Return Types
- Functions as Types
This chapter covers notes on TypeScript. What is TypeScript? It's a superset of JavaScript that provides some useful functionalities absent in vanilla JavaScript. These functionalities include various forms of syntactic sugar and, most importantly, type-checking.
As a JavaScript superset, everything we write in JavaScript can be written in TypeScript. But, browser cannot execute TypeScript. The TypeScript code must be transpiled to JavaScript first to affect changes.
Typechecking is TypeScript's most attractive feature. Consider the following example:
function add(number1, number2) {
return number1 + number2;
}
All this function does is add two numbers.
const result = add(1, 2);
console.log(result);
3
As it stands, the function above can take a variety of arguments. In particular, strings:
const result = add("1", "2");
console.log(result);
12
This is likely not the behavior we intended. We could solve this problem by introducing a guard against strings, throwing an error if string arguments are passed. And while that is a perfectly fine solution, it's a problem that's easily solved with type-checking. TypeScript solves that problem.
Types
TypeScript provides many different types. We'll start with the most basic types — the core types provided by vanilla JavaScript.
Number Type
JavaScript doesn't differentiate betweeen floats and integers. There's just
one type called number. Values of type number
include 1, 5.3, -10,
infinity
, -infinity
, 0
, and -0
.
With TypeScript, we can use the number type to specify that a function only takes number arguments:
function add(number1: number, number2: number) {
return number1 + number2;
}
Integer Checking
Although JavaScript doesn't provide an integer type, it does provide a
method called Number.isInteger()
which returns true if a number is an
integer and false otherwise. For example:
const x = Number.isInteger(5);
console.log(x);
const y = Number.isInteger(5.2);
console.log(y);
true
false
String Type
Strings have the type string
. In JavaScript, there are three ways to
specify strings: "string"
(using double-quotes), 'string'
(single-quotes), and `string`
(using backticks).
Just as we saw with the number type, we can specify parameters with the
string
type keyword:
function isEmptyString(str: string) {
return str === "";
}
Boolean Type
The Boolean values true
and false
can be type-checked with the type
boolean
.
function Nand(a: boolean, b: boolean) {
return !(a && b);
}
Type Inference
TypeScript provides a type-inference system. For example, TypeScript can infer all of the following:
const x = 5; // infers number
const y = true; // infers boolean
let n = ""; // infers string
For declared but uninitialized variables, it's considered best practice to indicate the variable's type upfront:
let x: number;
let y: boolean;
let n: string;
Object Types
Suppose we had the following JavaScript object:
const square = {
width: 5,
height: 5,
name: "square1",
};
TypeScript infers square
's type as:
{
width: number;
height: number;
name: string;
}
This works for nested objects as well.
const polygon = {
sideNames: ["a", "b", "c", "d"],
dimensions: {
length: 5,
width: 5,
},
name: "polygon1",
};
The inferred type:
{
sideNames: string[],
dimensions: {
length: number,
width: number
},
name: string,
};
Array Types
Arrays can also have types. As we know, JavaScript arrays can be homogenous (all of the array's elements are of one type) or heterogenous (the array consists of elements of various types).
Homogenous Arrays
Say we had the following array:
const arr = ["a", "b", "c"];
TypeScript infers this array's type as:
arr: string[];
Heterogenous Arrays
Heterogenous arrays, or mixed arrays, are arrays of different types. For example:
const arr = ["a", 1, true];
Generally speaking, mixed arrays are not safe. They're difficult to think
about and can lead to unexpected behavior. That said, we can we give these
arrays the type any
, a generic type:
arr: any[];
Tuple Types
Tuples are a data structure unique to TypeScript. They are not a part of vanilla JavaScript. Simply put, tuples are arrays of fixed length. Having fixed lengths is useful when we know that some datum should consist of a finite, ordered set of values. For example, the Cartesian coordinate takes the form With TypeScript, we can write:
cartesianCoordinate: [number, number];
Enums
Enums are a useful type found in various languages. They're what we should
use when we know that a particular value can only be one of a finite set.
For example, we might have data that can only be one of VOTER
and
NONVOTER
. In plain JavaScript, we might specify these as global
constants:
const VOTER = "voter";
const NONVOTER = "nonvoter";
This allows us to use the values VOTER
or NONVOTER
anywhere in our
code.
const VOTER = "voter";
const NONVOTER = "nonvoter";
if (user.registration === VOTER) {
user.castVote();
}
The problem with this approach is that we have to keep track of these constants. Not a huge problem if we only have a few such constants, but it can get unwieldy once we have several.
Fortunately, TypeScript makes this easy:
enum VoteStatus {
VOTER,
NONVOTER,
}
if (user.registration === VoteStatus.VOTER) {
user.castVote();
}
As with other languages, by default, the enum values map to numbers. Thus, the values above are really:
enum VoteStatus {
VOTER = 0,
NONVOTER = 1,
}
Of course, we can assign custom values:
enum VoteStatus {
VOTER = "voter",
NONVOTER = "nonvoter",
}
Union Types
Union types allow us to specify "or" type data. For example, we might have a function that takes a color value argument. That value could be a string (for an HTML named color) or a number (perhaps some HEX value). We could specify the function's parameter as:
function color(colorValue: string | number) {
if (typeof colorValue === "string") {
// code to execute if the value's a string
} else {
// code to execute if the value's a number
}
}
Type Aliasing
Some union types — e.g., number | string
— are so common that it'd be
cumbersome to write such types over and over again. Similar to languages
like ML, TypeScript allows us to write type aliases:
type Color = number | string;
function color(colorValue: Color) {
if (typeof colorValue === "string") {
// code to execute if the value's a string
} else {
// code to execute if the value's a number
}
}
Type aliases aren't limited to union types. We can use them for all the
previous types we've seen. For example, we might want to specify a
Cartesian coordinate as always having the type CartesianCoordinate
:
type CartesianCoordinate = [number, number];
Return Types
Some languages require functions to have return types (e.g., Rust, Swift, C, C++). Having explicit return types for functions makes our code much more predictable and reason about.
For example, a function that adds two numbers should always return a number:
function add(a: number, b: number): number {
return a + b;
}
Notice how we placed the type after the function's header. This is tells
TypeScript that our function add
should always return a number.
The Void Type
For functions that do not return values, we can use the void
type,
provided by TypeScript:
function printErrorCode(code: number): void {
console.log(code);
}
Functions as Types
TypeScript allows us to write function types. We can think of the function type as outlining what the function should take and return:
const f: () => number;
The code above tells TypeScript that the functione f
takes no parameters
and returns a number
. If the function f
does have parameters, say,
numbers, we can write:
const f: (a: number, b: number) => number;