Released · improving
TypeScript guide · 3/6
This chapter tours the building blocks you use to model data in TypeScript: primitive types, arrays and tuples, object types, interfaces versus type aliases, unions and literal types, enums, and classes.
Every JavaScript primitive has a matching type, always written in lowercase. The capitalized versions such as String refer to wrapper objects and should not be used as annotations.
| Type | Examples |
|---|---|
string | "hello", template literals |
number | 42, 3.14, NaN (no separate integer type) |
boolean | true, false |
bigint | 123n |
symbol | Symbol("id") |
null and undefined | The absence of a value |
A few special types round out the picture. any switches type checking off, so treat it as a last resort. unknown can also hold anything, but forces you to check what it is before use, which makes it the safe choice for untrusted input. void marks functions with no meaningful return value, and never describes code paths that cannot complete.
let id: bigint = 9007199254740993n;
let anything: unknown = JSON.parse('{"a":1}');
// anything.a; // error: narrow an unknown before using it
if (typeof anything === "object" && anything !== null) {
console.log("it is an object");
}
function fail(message: string): never {
throw new Error(message); // never returns normally
}Write array types as Type[] or Array<Type>. Add readonly when the array should not be mutated. A tuple is an array with a fixed length and a known type at each position.
const tags: string[] = ["ts", "web"];
const ids: Array<number> = [1, 2, 3];
const frozen: readonly string[] = ["a", "b"];
// frozen.push("c"); // error: readonly array
const point: [number, number] = [10, 20];
const entry: [string, number] = ["age", 30];
const [key, value] = entry; // key: string, value: number
function minMax(values: number[]): [number, number] {
return [Math.min(...values), Math.max(...values)];
}Give an object shape a name with an interface or a type alias. A trailing ? makes a property optional, and readonly prevents reassignment.
interface User {
readonly id: number;
name: string;
email?: string; // may be missing
}
interface Admin extends User { // extend an interface
permissions: string[];
}
type Point = { x: number; y: number };
type Point3D = Point & { z: number }; // combine with an intersection
const admin: Admin = { id: 1, name: "Kim", permissions: ["write"] };
// admin.id = 2; // error: read-only propertyTypeScript's type system is structural: if a value has every property a type requires, it fits, regardless of what the type is called. Here is how the two declaration styles compare:
| Capability | interface | type |
|---|---|---|
| Describe an object shape | Yes | Yes |
| Extend another type | extends | Intersection with & |
| Merge repeated declarations | Yes | No |
| Alias unions, tuples, primitives | No | Yes |
A common convention is interface for object shapes and type for everything else, such as unions. Consistency matters more than the choice itself.
A union type means "one of these", written with | between the members. Combined with literal types, unions let you spell out exactly which values are allowed.
type Id = number | string;
type Direction = "up" | "down" | "left" | "right";
function move(direction: Direction, steps = 1) {
console.log(`moving ${direction} by ${steps}`);
}
move("up");
// move("forward"); // error: not a valid Direction
function printId(id: Id) {
if (typeof id === "string") {
console.log(id.toUpperCase()); // id is a string here
} else {
console.log(id.toFixed(0)); // and a number here
}
}The way the typeof check refines id inside each branch is called narrowing. Chapter 6 covers it in depth.
An enum is a named set of constants. Both numeric and string enums exist; string enums are easier to debug because the values are readable at runtime. Enums are one of the few TypeScript features that generate real JavaScript, so tools that only erase types may not handle them; many projects prefer an as const object paired with a union type.
enum Status {
Active = "ACTIVE",
Paused = "PAUSED",
}
const s: Status = Status.Active;
const Color = {
Red: "red",
Green: "green",
} as const;
type Color = (typeof Color)[keyof typeof Color]; // "red" or "green"
const c: Color = Color.Red;In a class you declare properties with types and control access with public, private, and protected. Putting a modifier on a constructor parameter declares and assigns the property in one step. implements asks the compiler to verify that the class satisfies an interface.
interface Shape {
area(): number;
}
class Rectangle implements Shape {
constructor(
private readonly width: number,
private readonly height: number,
) {}
area(): number {
return this.width * this.height;
}
}
class Counter {
#count = 0; // a native JavaScript private field
increment(): number {
return ++this.#count;
}
}
const shapes: Shape[] = [new Rectangle(2, 3)];
console.log(shapes[0].area()); // 6private is enforced only by the compiler, while # fields stay private at runtime as well.
string, number, boolean, and so on); prefer unknown over any.T[] for arrays, tuples like [A, B] for fixed shapes, and readonly to prevent mutation.interface and compose other types with type.as const object is a lightweight alternative to enum.implements checks.
0 comments
Sign in · Sign in to leave a comment.
Be the first to comment.