TypeScript Syntax and Basic Types

1. Type Annotations and Type Inference

Feature Syntax Description Example
Type Annotation variable: Type Explicitly specify variable type - provides documentation and compile-time checking let age: number = 25;
Type Inference let x = value TypeScript automatically infers type from initial value - reduces verbosity let name = "John";
Function Return Type function(): Type Annotate function return type for clarity and type safety function getAge(): number
Parameter Annotation param: Type Required for function parameters - inference not available function(x: string)
Contextual Typing callback context Type inferred from context where value is used - common in callbacks arr.map(x => x.length)

Example: Type annotations vs inference

// Explicit annotation
let count: number = 10;
let message: string = "Hello";

// Type inference (recommended when obvious)
let inferredCount = 10;        // number
let inferredMsg = "Hello";     // string

// Function with annotations
function add(a: number, b: number): number {
    return a + b;
}

// Contextual typing in callbacks
const numbers = [1, 2, 3];
numbers.map(num => num * 2);  // num inferred as number

2. Primitive Types

Type Syntax Values Use Case
string string Text values, template literals, Unicode characters Any textual data - names, messages, HTML
number number Integers, floats, Infinity, NaN, hex, binary, octal All numeric operations - counts, calculations, IDs
boolean boolean true or false Flags, conditions, toggles, binary states
null null Intentional absence of value Explicit "no value" - cleared references
undefined undefined Uninitialized or missing value Default unassigned state, optional params
symbol symbol Unique immutable primitive value Object keys, unique identifiers, constants
bigint ES2020 bigint Arbitrarily large integers beyond Number.MAX_SAFE_INTEGER Large number calculations, cryptography

Example: Primitive type usage

// String types
let username: string = "Alice";
let template: string = `Hello ${username}`;

// Number types
let age: number = 30;
let price: number = 19.99;
let hex: number = 0xf00d;
let binary: number = 0b1010;

// Boolean
let isActive: boolean = true;
let hasPermission: boolean = false;

// Null and undefined
let nothing: null = null;
let notAssigned: undefined = undefined;

// Symbol
let uniqueId: symbol = Symbol("id");

// BigInt
let huge: bigint = 9007199254740991n;
Note: In strict mode (strictNullChecks: true), null and undefined are not assignable to other types unless explicitly allowed via union types.

3. Literal Types and Template Literal Types

Type Syntax Description Example
String Literal "value" Exact string value as type - restricts to specific text let dir: "left" | "right"
Numeric Literal 42 Exact number as type - useful for constants and configs let code: 200 | 404
Boolean Literal true | false Specific boolean value - often redundant but useful in unions let success: true
Template Literal Type TS 4.1+ `prefix-${T}` String patterns with type interpolation - generates string combinations `user-${number}`
Uppercase Utility Uppercase<T> Converts string literal to uppercase Uppercase<"hello">
Lowercase Utility Lowercase<T> Converts string literal to lowercase Lowercase<"WORLD">
Capitalize Utility Capitalize<T> Capitalizes first character Capitalize<"name">
Uncapitalize Utility Uncapitalize<T> Lowercases first character Uncapitalize<"Name">

Example: Literal types for type safety

// String literals
type Direction = "north" | "south" | "east" | "west";
let move: Direction = "north";

// Numeric literals
type HttpCode = 200 | 201 | 400 | 404 | 500;
let response: HttpCode = 200;

// Template literal types
type EventName = `on${Capitalize<string>}`;
type UserId = `user-${number}`;
type CSSProperty = `--${string}`;

// Practical template literal usage
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type Endpoint = `/${string}`;
type APIRoute = `${HTTPMethod} ${Endpoint}`;

let route: APIRoute = "GET /api/users";

4. Array Types and Tuple Types

Type Syntax Description Use Case
Array Syntax 1 Type[] Preferred array notation - clean and readable let nums: number[] = [1, 2, 3]
Array Syntax 2 Array<Type> Generic array notation - useful with complex types Array<string | number>
Readonly Array readonly Type[] Immutable array - prevents modifications readonly number[]
Tuple [Type1, Type2] Fixed-length array with specific types per position [string, number]
Optional Tuple Element [Type, Type?] Tuple element may be undefined - increases flexibility [string, number?]
Rest in Tuple [Type, ...Type[]] Variable-length tuple with typed rest elements [string, ...number[]]
Labeled Tuple TS 4.0+ [name: Type] Named tuple elements for better documentation [x: number, y: number]

Example: Arrays and tuples

// Basic arrays
let numbers: number[] = [1, 2, 3, 4];
let strings: Array<string> = ["a", "b", "c"];

// Multi-dimensional arrays
let matrix: number[][] = [[1, 2], [3, 4]];

// Readonly arrays
let immutable: readonly string[] = ["a", "b"];
// immutable.push("c"); // Error!

// Tuples
let coordinate: [number, number] = [10, 20];
let user: [string, number, boolean] = ["Alice", 30, true];

// Optional tuple elements
let response: [number, string?] = [200];
let fullResponse: [number, string?] = [200, "OK"];

// Rest elements in tuples
let data: [string, ...number[]] = ["label", 1, 2, 3];

// Labeled tuples
type Point = [x: number, y: number];
let point: Point = [10, 20];

5. Object Types and Index Signatures

Feature Syntax Description Example
Object Type { prop: Type } Inline object shape definition - quick for simple objects { name: string }
Optional Property prop?: Type Property may be undefined or missing { age?: number }
Readonly Property readonly prop Property cannot be reassigned after initialization readonly id: string
Index Signature [key: Type]: Type Dynamic keys with consistent value type - flexible dictionaries [key: string]: number
String Index [key: string]: T Any string key maps to type T - most common pattern [id: string]: User
Number Index [index: number]: T Numeric indices - array-like objects [index: number]: Item
Symbol Index TS 4.4+ [key: symbol]: T Symbol keys for unique properties [key: symbol]: any
Template Literal Key TS 4.4+ [K: `prop${T}`]: V Pattern-based property keys [K: `data${string}`]: T

Example: Object types and index signatures

// Basic object type
let user: { name: string; age: number } = {
    name: "John",
    age: 30
};

// Optional and readonly properties
let config: {
    host: string;
    port?: number;
    readonly apiKey: string;
} = {
    host: "localhost",
    apiKey: "abc123"
};

// Index signatures
let scores: { [student: string]: number } = {
    "Alice": 95,
    "Bob": 87
};

// Mixed: defined properties + index signature
type Dictionary = {
    count: number;  // known property
    [key: string]: number;  // dynamic keys
};

// Nested objects
let company: {
    name: string;
    address: {
        street: string;
        city: string;
    };
} = {
    name: "TechCorp",
    address: { street: "Main St", city: "NYC" }
};

6. Union and Intersection Types

Type Syntax Description Use Case
Union Type Type1 | Type2 Value can be one of several types - logical OR string | number
Intersection Type Type1 & Type2 Value must satisfy all types - logical AND Person & Employee
Discriminated Union type with literal Union with common discriminant property for type narrowing { kind: "a" } | { kind: "b" }
Union with null Type | null Allow null values explicitly - common with strict null checks string | null
Union with undefined Type | undefined Allow undefined values - optional-like behavior number | undefined
Multi-way Union A | B | C | D More than two types in union - common for enums alternative "left" | "right" | "up"

Example: Union types for flexibility

// Basic union
let id: string | number;
id = "ABC123";  // OK
id = 42;        // OK

// Union with null
function find(name: string): User | null {
    return database.find(name) ?? null;
}

// Discriminated union (tagged union)
type Success = { status: "success"; data: any };
type Error = { status: "error"; message: string };
type Result = Success | Error;

function handle(result: Result) {
    if (result.status === "success") {
        console.log(result.data);  // Type narrowed to Success
    } else {
        console.log(result.message); // Type narrowed to Error
    }
}

Example: Intersection types for composition

// Basic intersection
type Person = { name: string; age: number };
type Employee = { employeeId: string; department: string };
type Worker = Person & Employee;

let worker: Worker = {
    name: "John",
    age: 30,
    employeeId: "E123",
    department: "IT"
};

// Mixin pattern with intersections
type Timestamped = { createdAt: Date; updatedAt: Date };
type Versioned = { version: number };
type Document = { id: string; content: string };

type FullDocument = Document & Timestamped & Versioned;

7. any, unknown, never, and void Types

Type Behavior Description Use Case
any Disables type checking Escape hatch - allows any operation without type safety Migration, third-party libs, quick prototypes
unknown TS 3.0+ Type-safe any Requires type checking before use - safer than any User input, API responses, dynamic data
never Represents impossible Functions that never return, unreachable code, exhaustive checks Error throwing, infinite loops, type guards
void No return value Function completes but returns nothing meaningful Event handlers, loggers, side-effect functions

any vs unknown

Feature any unknown
Type Safety None Full
Operations All allowed Requires checks
Assignment To/from any type From any, to unknown only
Recommendation Avoid if possible Preferred over any

void vs never

Feature void never
Return Function completes Never completes
Value undefined No value possible
Usage Side effects Errors, unreachable

Example: any vs unknown

// any - no type safety
let data: any = getUserInput();
data.anything();        // No error, dangerous!
data.toUpperCase();     // Runtime error if not string

// unknown - type-safe
let safeData: unknown = getUserInput();
// safeData.anything(); // Error! Must check first

if (typeof safeData === "string") {
    safeData.toUpperCase(); // OK, narrowed to string
}

Example: never and void

// void - function completes but returns nothing
function log(message: string): void {
    console.log(message);
    // implicitly returns undefined
}

// never - function never returns
function throwError(message: string): never {
    throw new Error(message);
    // execution stops here
}

function infiniteLoop(): never {
    while (true) {
        // never exits
    }
}

// never in exhaustive checks
type Shape = "circle" | "square";
function assertNever(x: never): never {
    throw new Error("Unexpected: " + x);
}

function getArea(shape: Shape): number {
    switch (shape) {
        case "circle": return 1;
        case "square": return 2;
        default: return assertNever(shape); // Compile error if not exhaustive
    }
}
Warning: Avoid any whenever possible - it defeats TypeScript's purpose. Use unknown when you need dynamic typing with safety.