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 |
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.