TypeScript: popular alternative to JavaScript
supports static typing → better autocompletion and error detection
data types may be specified explicitly by the developer or inferred by the development invironment
benefits:
example:
let names: Array<string> = [];
names.push('Alice');
names.push('Bob');
console.log(names[0].toUpperCase());
experiment with TypeScript online:
browsers only understand JavaScript, not TypeScript
→ code needs to be translated from TypeScript to JavaScript to run in the browser
example project setup with node / npm and vite:
npm create vite@latest
→ select Vanilla, then TypeScript
data types and declarations that we'll use:
variable types can be specified when declaring variables:
let age: number = 32;
in many cases, TypeScript will know (infer) a type automatically (type annotation not needed):
let age = 32;
const age: number = 32;
const name: string = 'Alice';
const loggedIn: boolean = true;
const names: Array<string> = [];
names.push('Alice');
alternative syntax:
const names: string[] = [];
names.push('Alice');
type declaration:
let todo: {
id: number;
title: string;
completed: boolean;
};
assignment:
todo = { id: 1, title: 'foo', completed: false };
optional entries are marked with ?
let todo: {
id: number;
title: string;
completed: boolean;
date?: string;
};
variables that can be one of multiple types:
let width: string | number | undefined;
width = '16px';
width = 16;
Type aliases and interfaces: similar techniques that allow us to store a type declaration under a name
Type aliases can be somewhat more flexible and easy to use (comparison on StackOverflow)
type alias for objects:
type Todo = {
id: number;
title: string;
completed: boolean;
};
interface for objects:
interface Todo {
id: number;
title: string;
completed: boolean;
}
const todos: Array<Todo> = [
{ id: 1, title: 'foo', completed: false },
{ id: 2, title: 'bar', completed: true },
];
type aliases and interfaces should be capitalized (e.g. Todo
, not todo
)
function shorten(text: string, maxLen: number): string {
// ...
}
const shorten = (text: string, maxLen: number): string => {
// ...
};
Functions without a return value: void
function logMessage(message: string): void {
console.log(message);
}
storing a function signature under a type alias:
type Validator = (s: string) => boolean;
applying the type alias:
const validateEmail: Validator = (s) => s.includes('@');
const validateYear: Validator = (s) => {
return new RegExp('^d{4}$').test(s);
};
Generics: type declarations that can receive more specific type information when applied (via <...>
)
example: Array
is a generic
const names: Array<string> = ['Alice', 'Bob', 'Charlie'];
example: Promise
is a generic
type User = { username: string; picture: string };
async function fetchUser(): Promise<User> {
// ...
}
With function calls that access "external" resources, TypeScript may be unable to correctly infer the type on its own
examples:
fetch()
JSON.parse()
document.getElementById()
Type assertions allow us to treat an existing object as a specific type
this fails:
// TypeScript type: HTMElement or null
const nameInput = document.getElementById('name-input');
// error: nameInput is possibly null
console.log(nameInput.id);
// error: nameInput is possibly null
console.log(nameInput.value);
asserting that a value is not null or undefined by using "!":
// TypeScript type: HTMLElement
const nameInput = document.getElementById('name-input')!;
// works
console.log(nameInput.id);
// error: property `value` does not exist on type `HTMLElement`
console.log(nameInput.value)
asserting that a value has a specific type by using "as":
// TypeScript type: HTMLInputElement
const nameInput = document.getElementById(
'name-input'
) as HTMLInputElement;
// works
console.log(nameInput.id);
// works
console.log(nameInput.value);
(note: HTMLInputElement
is a subclass of HTMLElement
)
example: fetching JSON data
type User = { username: string; picture: string };
async function fetchUser(): Promise<User> {
const res = await fetch('/api/user');
if (!res.ok) {
throw new Error('fetch failed');
}
const user = (await res.json()) as User;
return user;
}