TypeScript Best Practices

Chapter 2 · Practice 1 of 6

Annotate function boundaries and let TypeScript infer the rest

From Modeling data with types

Why it matters

TypeScript infers most local types correctly. Writing const count: number = 0 adds noise without adding safety. Annotations earn their place at boundaries, where they act as a contract:

  • function parameters, which TypeScript cannot infer,
  • return types of exported functions, so a change inside the function cannot silently change what callers receive,
  • exported constants and public object shapes.

An explicit return type also moves errors to the right place. Without it, a mistake in the function body shows up as a confusing error in some caller. With it, the error points at the line that broke the contract.

Do

  • Annotate parameters and the return types of exported functions
  • Let locals, loop variables and callback parameters be inferred

Avoid

  • Annotating every local variable with a type TypeScript already knows
  • Letting a public function return a different type because of an edit deep inside it

Example

TypeScript
// The return type is the contract callers depend on
export function parsePrice(input: string): number | null {
  const value = Number.parseFloat(input); // inferred: number
  return Number.isFinite(value) ? value : null;
}