Home/Learn/JavaScript & TypeScript/Basic Types, Inference and Annotations

Basic Types, Inference and Annotations

Beginner
TypeScript

Annotate function boundaries and let inference handle the rest. Over-annotating is a common beginner habit that makes code noisier without making it safer.

Overview

TypeScript infers far more than people coming from Java expect. Declaring `const x: number = 5` adds nothing — the compiler already knew. The rule that works in practice is to annotate the boundaries where inference cannot reach or where you want the compiler to hold you to a contract: function parameters, exported return types, and empty containers. Everything in between should be inferred, because inferred types stay correct when the code changes and hand-written ones drift.

The Primitives

Lowercase names for primitives. The capitalised ones are wrapper objects and you almost never want them.

any is an escape hatch; unknown is the safe one
let name: string = 'Akshay'
let count: number = 42            // one number type — no int/long/float
let big: bigint = 9007199254740993n
let ok: boolean = true
let nothing: null = null
let missing: undefined = undefined
let key: symbol = Symbol('id')

// Arrays and tuples
let scores: number[] = [90, 85]
let pair: [string, number] = ['arrays', 12]        // fixed length and order
let named: [x: number, y: number] = [1, 2]         // labels, for readability

// any switches off checking; unknown keeps it on
let a: any = getValue()
a.whatever.deeply.nested       // compiles, may explode

let u: unknown = getValue()
u.whatever                     // Error — must narrow first
if (typeof u === 'string') u.toUpperCase()   // now allowed

// never — the type with no values; the return type of a function that throws
function fail(msg: string): never { throw new Error(msg) }

Inference vs Annotation

Where to annotate and where not to.

Annotate boundaries, infer the middle
// Redundant — inference already got it
const count: number = 0
const names: string[] = ['a', 'b']

// Just write
const count = 0                  // number
const names = ['a', 'b']         // string[]

// DO annotate: parameters, and exported return types
export function score(attempts: Attempt[], weight: number): Readiness {
  ...
}
// The return annotation makes the compiler check the body against the
// contract, so a mistake is reported here rather than at every call site.

// DO annotate: empty containers, which infer as never[] or {}
const seen: string[] = []
const cache: Record<string, User> = {}

// let vs const widening
const dir = 'up'      // type is 'up'  — the literal
let dir2 = 'up'       // type is string — widened
// which is why const works where a literal type is required

Functions

Optional parameters, defaults, rest, and typing a function value.

Contextual typing means callbacks infer themselves
function paginate(page: number, size = 20, sort?: string) {
  // size is number (inferred from the default)
  // sort is string | undefined
}

function sum(...nums: number[]): number {
  return nums.reduce((a, b) => a + b, 0)
}

// Typing a variable that holds a function
type Comparator = (a: Problem, b: Problem) => number
const byDifficulty: Comparator = (a, b) => a.level - b.level
// Parameters need no annotation — they are inferred from Comparator.
// This is "contextual typing" and it is why callbacks rarely need types.

items.filter((item) => item.active)      // item inferred from items

// void return means "ignore whatever this returns"
type Handler = (e: Event) => void
const h: Handler = (e) => true    // allowed; the boolean is discarded

Key Points to Remember

  • 1TypeScript has one number type — no int, long, float or double distinction
  • 2Annotate function parameters and exported return types; let everything else infer
  • 3any disables checking entirely; unknown forces you to narrow before use and is the safe alternative
  • 4const infers a literal type while let widens to the primitive, which matters for unions
  • 5Callback parameters are contextually typed from the surrounding signature, so they rarely need annotations

Interview Questions

Sign in to ask Aria
1

What is the difference between any and unknown?

Medium
2

Why does const x = "up" have a different type than let x = "up"?

Medium
3

When is it worth annotating a function's return type rather than letting it infer?

Medium

Ask Aria about Basic Types, Inference and Annotations

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…