TS

Type Annotations

TypeScript · 6 entries

Variable Annotations

syntax
let varName: Type = value;
const varName: Type = value;
example
let orderId: string = "ORD-7821";
const maxRetries: number = 3;
let tags: string[] = ["urgent", "billing"];

// Often unnecessary when the type is obvious
let count = 0; // inferred as number
const label = "total"; // inferred as literal "total"
output
// Explicit annotations override inference when needed

Note Let TypeScript infer when the type is obvious from the initializer. Add explicit annotations when: the inferred type is too wide, you are declaring without initializing, or you want documentation clarity.

Function Parameter Types

syntax
function fn(param: Type, param2: Type): ReturnType { ... }
example
function calculateTotal(
  items: { name: string; price: number }[],
  taxRate: number
): number {
  const subtotal = items.reduce((sum, item) => sum + item.price, 0);
  return subtotal * (1 + taxRate);
}
output
// Parameters MUST be annotated — TypeScript does not infer param types

Note Function parameters are never inferred from usage. Always annotate them. The return type is usually inferred correctly, but annotating it explicitly catches mistakes when the function body changes.

Return Type Annotations

syntax
function fn(): ReturnType { ... }
const fn = (): ReturnType => { ... };
example
function findUser(id: string): User | undefined {
  return userDatabase.get(id);
}

// Arrow function return type
const formatCurrency = (amount: number): string => {
  return `$${amount.toFixed(2)}`;
};

// Async function
async function fetchOrder(id: string): Promise<Order> {
  const resp = await fetch(`/api/orders/${id}`);
  return resp.json();
}
output
// Async functions always return Promise<T>

Note Explicit return types are especially valuable for public API functions, async functions, and functions with multiple return paths. They also speed up type-checking in large projects since the compiler does not need to analyze the function body.

Type Inference

syntax
// No annotation needed — TypeScript figures it out
let varName = value;
example
let customerName = "Kenji"; // string
const port = 8080; // literal type 8080 (const narrows)

let results = [1, 2, 3]; // number[]
let mixed = [1, "two", true]; // (string | number | boolean)[]

// Return type inference
function double(n: number) {
  return n * 2; // inferred return: number
}
output
// const uses literal types; let uses widened types

Note const declarations infer the narrowest (literal) type, while let declarations widen to the base type. This is called 'widening'. Use 'as const' to get literal types with let, or on objects/arrays to make them deeply readonly with literal types.

Type Assertions

syntax
value as Type
<Type>value  // not in JSX files
example
const rawData = JSON.parse(payload) as { userId: string; role: string };

// HTMLElement narrowing
const canvas = document.getElementById("game") as HTMLCanvasElement;
const ctx = canvas.getContext("2d");

// Double assertion for incompatible types (use sparingly)
const weird = ("hello" as unknown) as number;
output
// Assertions override the compiler — they do NOT perform runtime conversion

Note Type assertions are a compile-time escape hatch — no runtime checking or conversion happens. If you assert incorrectly, you get silent bugs. Prefer type guards (typeof, instanceof) when possible. The angle-bracket syntax <Type>value conflicts with JSX — always use 'as Type' in .tsx files.

Function Overloads

syntax
function fn(param: TypeA): ReturnA;
function fn(param: TypeB): ReturnB;
function fn(param: TypeA | TypeB): ReturnA | ReturnB { ... }
example
function parseInput(input: string): string[];
function parseInput(input: number): number[];
function parseInput(input: string | number): string[] | number[] {
  if (typeof input === "string") {
    return input.split(",");
  }
  return Array.from({ length: input }, (_, i) => i);
}

const words = parseInput("a,b,c"); // string[]
const nums = parseInput(5);        // number[]
output
// Caller sees the specific overload, not the implementation signature

Note The implementation signature is not callable directly — only the overload signatures are visible to callers. Order matters: TypeScript picks the first matching overload. Put more specific signatures before general ones.