Reading — step 1 of 8
Learn
TypeScript's superpower is types you write down once, checked everywhere. Catching a typo at compile time beats discovering it at 3am in production. This lesson covers the core type vocabulary you'll use every day.
The basic primitives
let count: number = 0;
let name: string = "Alice";
let active: boolean = true;
let nothing: null = null;
let missing: undefined = undefined;
Lowercase number, string, boolean. (Capitalized Number, String, Boolean are the wrapper objects — almost never what you want.)
Inference — let TypeScript figure it out
let count = 0; // inferred as number
let name = "Alice"; // inferred as string
const pi = 3.14; // inferred as the literal type 3.14
For let, the inferred type is WIDENED to the general type (number, string) — the variable can be reassigned, so any number must be allowed. For const primitives, TypeScript infers the exact literal type (3.14, "Alice", true), because the value can never change.
Rule: only annotate function signatures, exported APIs, and ambiguous cases. Annotating const x: number = 5 is noise — TypeScript already knows.
Arrays
let scores: number[] = [90, 85, 100];
let names: string[] = ["Alice", "Bob"];
let matrix: number[][] = [[1, 2], [3, 4]];
// Generic syntax (equivalent):
let scores2: Array<number> = [90, 85, 100];
Most code uses T[] — shorter and reads naturally.
Object types
let user: { name: string; age: number } = {
name: "Alice",
age: 30,
};
For reusable shapes, use an interface or type (covered later in this chapter).
Tuples — fixed-length, typed positions
let pair: [string, number] = ["Alice", 30]; // exactly 2 elements
pair[0]; // string
pair[1]; // number
Tuples are arrays where each position has a specific type and the length is fixed. Useful for return values like [error, data] or coordinates.
Union types — "this OR that"
let id: string | number = 42;
id = "abc"; // also OK
id = true; // ✗ compile error
Unions express "could be one of these." Pair with type narrowing to handle each case:
function format(id: string | number): string {
if (typeof id === "string") {
return id.toUpperCase(); // TypeScript knows id is string here
}
return id.toString(); // and number here
}
Literal types
let status: "active" | "inactive" | "pending" = "active";
status = "foo"; // ✗ compile error — only those three values allowed
A literal type IS a value, used as a type. Combined with unions, it becomes an enum-like "one of these strings."
any — the escape hatch (avoid)
let x: any = 5;
x = "hello"; // OK
x.notAFunction(); // OK at compile time, BOOM at runtime
any opts out of type checking entirely. Useful for migrating JS code to TS, but defeats the purpose elsewhere. Treat any usage of any as a TODO.
unknown — the type-safe any
let x: unknown = JSON.parse(input);
x.length; // ✗ compile error — you must narrow first
if (typeof x === "string") {
x.length; // ✓ safe — narrowed to string
}
Use unknown when you really don't know the shape. The compiler forces you to narrow before using.
void and never
function logIt(msg: string): void { // void = returns nothing
console.log(msg);
}
function throwError(): never { // never = function never returns normally
throw new Error("!");
}
void for functions that don't return a value. never for functions that throw or loop forever — and as the type of impossible cases (exhaustiveness checks).
Type assertions — "trust me"
const el = document.getElementById("my-input") as HTMLInputElement;
el.value; // OK because of the assertion
as Type tells TypeScript to TREAT the value as that type. No runtime check — if you're wrong, you get an error later. Use sparingly.
Common mistakes
- Using
anyto silence errors — fix the underlying issue or useunknown+ narrowing. - Annotating obvious things:
const x: number = 5is noise. - Forgetting that primitives are lowercase —
Number,String,Booleanare wrappers (avoid). - Confusing
unknownandany—unknownforces narrowing;anyskips checks.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…