Difficulty: Beginner
What are the basic types in TypeScript? Explain type annotations and type inference.
TypeScript provides several primitive types that map to JavaScript values, plus additional types for safer code:
Primitive types: string, number, boolean, null, undefined, bigint, symbol Special types: void (no return), never (unreachable), unknown (safe any), any (escape hatch)
Type annotations explicitly declare a variable's type with a colon syntax. Type inference lets TypeScript automatically determine the type from the assigned value. Best practice: let TypeScript infer when obvious, annotate when the type isn't clear from context (function parameters, return types, complex expressions).
// Explicit type annotations
let name: string = 'Alice';
let age: number = 30;
let isActive: boolean = true;
let nothing: null = null;
let notDefined: undefined = undefined;
let big: bigint = 100n;
// Type inference - TypeScript figures it out
let city = 'New York'; // inferred as string
let count = 42; // inferred as number
let done = false; // inferred as boolean
// const gets literal types
const status = 'active'; // type: 'active' (not string)
const maxRetries = 3; // type: 3 (not number)
// Arrays
let numbers: number[] = [1, 2, 3];
let names: Array<string> = ['Alice', 'Bob'];
let mixed: (string | number)[] = [1, 'two', 3];
Use annotations when the type isn't obvious. Let TypeScript infer when the value makes the type clear. const declarations get narrower literal types.
// Parameter and return type annotations
function add(a: number, b: number): number {
return a + b;
}
// Arrow function with types
const multiply = (a: number, b: number): number => a * b;
// Optional and default parameters
function greet(name: string, greeting?: string): string {
return `${greeting ?? 'Hello'}, ${name}!`;
}
greet('Alice'); // OK: 'Hello, Alice!'
greet('Alice', 'Hi'); // OK: 'Hi, Alice!'
// Rest parameters
function sum(...nums: number[]): number {
return nums.reduce((a, b) => a + b, 0);
}
// void return type
function logMessage(msg: string): void {
console.log(msg);
// no return value
}
Always annotate function parameters. Return types can often be inferred, but explicit annotations catch accidental return type changes.
// void - function returns nothing
function log(msg: string): void {
console.log(msg);
}
// never - function never returns (throws or infinite loop)
function throwError(msg: string): never {
throw new Error(msg);
}
function infiniteLoop(): never {
while (true) {
// runs forever
}
}
// unknown - safe version of any (must narrow before use)
function processInput(input: unknown): string {
// input.toUpperCase(); // Error: Object is of type 'unknown'
if (typeof input === 'string') {
return input.toUpperCase(); // OK after narrowing
}
if (typeof input === 'number') {
return input.toString();
}
return String(input);
}
// any - disables type checking (avoid!)
let risky: any = 'hello';
risky.nonexistent.method(); // No error, crashes at runtime
Prefer unknown over any when the type is truly unknown. unknown forces you to narrow before use, keeping your code safe.
Primitive Types, Type Annotations, Type Inference, void, null, undefined