When to Add `: Type` (and When Not To)
Annotations
Annotations explicitly declare a value's type. They're required in some places, optional in others. Knowing where to add them is half of writing good TS.
What you'll learn
- Add annotations to variables, parameters, returns
- Know when annotations are required
- Let inference do the rest
A type annotation is : Type after a name. It tells TS what type
something is. Annotations are required in a few places and optional
(but sometimes helpful) elsewhere.
Where Annotations Live
let count: number = 0; // variable
function add(a: number, b: number): number {
return a + b;
}
// ^ parameter ^ return
const greet = (name: string): string => `Hi, ${name}`; Required: Function Parameters
Parameters CANNOT be inferred from inside the function alone — TS can’t know what callers will pass. So you annotate them:
function double(n: number) { // n must be annotated
return n * 2;
} The return type CAN be inferred from n * 2 — annotation
optional. Many teams annotate returns anyway, to make API
intent explicit.
Optional: Variables With Initializers
When you initialize a variable, TS infers the type:
let name = "Ada"; // inferred: string
let age = 36; // inferred: number
let admin = true; // inferred: boolean The redundant annotations would just clutter:
let name: string = "Ada"; // ✗ unnecessary
let name = "Ada"; // ✓ better Required-ish: Empty Initializers
let names: string[] = []; // type otherwise becomes `never[]` / `any[]`
let user: User | null = null; When the initial value doesn’t reflect the eventual type, annotate.
Annotations as Contracts
For exported / public functions, annotating returns is good discipline — it documents intent AND prevents accidental changes:
export function fetchUser(id: string): Promise<User> {
return fetch(`/api/users/${id}`).then(r => r.json());
} If you accidentally make this return something else, the compiler flags it. Without the annotation, the implementation defines the contract.
Wide vs Narrow Inference
let variables infer wide; const variables infer narrow:
let kind = "admin"; // string
const kind = "admin"; // "admin" (literal type) We come back to this in the literal types lesson.
Rule of Thumb
- Parameters: ALWAYS annotate
- Exported function returns: annotate (documentation)
- Local variables with literal initializers: don’t annotate
- Empty arrays /
nullinitial states: annotate - Type-inferred everything else: don’t annotate
Up Next
The flip side — what TypeScript can infer for free.
Inference →