Primitive Types
Primitive Types
TypeScript has primitive types that map to JavaScript runtime types.
Number, String, Boolean
let age: number = 28;
let score: number = 99.5;
let hex: number = 0xff;
let big: number = 1_000_000;
let name: string = 'Alice';
let greeting: string = `Hello, ${name}`;
let isActive: boolean = true;
Null and Undefined
let nothing: undefined = undefined;
let empty: null = null;
// With strictNullChecks, these are distinct types
function greet(name: string | null): string {
if (name === null) return 'Hello, stranger!';
return `Hello, ${name}!`;
}
interface Config {
timeout?: number; // number | undefined
}
Void
function logMessage(msg: string): void {
console.log(msg);
}
const doNothing: void = undefined;
Special Types
Special Types: any, unknown, never
any
Disables type checking entirely. Use as a last resort.
let dangerous: any = 'hello';
dangerous = 42;
dangerous.nonExistentMethod(); // runtime crash
let result: string = dangerous; // no compile error
unknown
Safest alternative to any. Must narrow before using.
let data: unknown = fetchFromAPI();
if (typeof data === 'string') {
console.log(data.toUpperCase()); // safe
}
let str: string = data; // Error: cannot assign unknown to string
never
Represents impossible values or functions that never return.
function throwError(msg: string): never {
throw new Error(msg);
}
type Shape = 'circle' | 'square' | 'triangle';
function getArea(shape: Shape): number {
switch (shape) {
case 'circle': return Math.PI * 100;
case 'square': return 100;
case 'triangle': return 50;
default:
const _exhaustive: never = shape;
return _exhaustive;
}
}
Annotations vs Inference
Type Annotations vs Inference
TypeScript can infer types automatically.
When Inference Works
let x = 10; // inferred: number
let name = 'Alice'; // inferred: string
let nums = [1, 2, 3]; // inferred: number[]
let person = { name: 'Bob', age: 30 }; // inferred object
const double = (x: number) => x * 2; // inferred return type
When to Add Annotations
// Return type helps catch accidental changes
function calculateTotal(items: number[]): number {
return items.reduce((sum, n) => sum + n, 0);
}
// Variables declared without initialization
let result: string;
if (condition) { result = 'yes'; }
else { result = 'no'; }
Const Assertions
let direction = 'up'; // type: string
const dir2 = 'up' as const; // type: 'up' literal
direction = 'down'; // OK
// dir2 = 'down'; // Error
const point = [10, 20] as const; // readonly [10, 20]
Type Assertions
let input = document.getElementById('name') as HTMLInputElement;
input.value = 'Alice';
Literal Types and BigInt
Literal Types and BigInt
Literal Types
type Direction = 'up' | 'down' | 'left' | 'right';
let move: Direction = 'up';
// move = 'forward'; // Error
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
let roll: DiceRoll = 3;
type Success = { ok: true; data: string };
type Failure = { ok: false; error: string };
type Result = Success | Failure;
function handleResult(result: Result) {
if (result.ok) {
console.log(result.data);
} else {
console.log(result.error);
}
}
BigInt
let huge: bigint = 9007199254740991n;
let alsoHuge: bigint = BigInt(9007199254740991);
// BigInt cannot be mixed with number
// let mixed = huge + 1; // Error
let result = huge + 1n; // OK
Symbol
const sym1 = Symbol('id');
const sym2 = Symbol('id');
sym1 === sym2; // false - each Symbol is unique
// Useful as unique object keys
const SECRET = Symbol('secret');
const obj = {
[SECRET]: 'hidden value',
public: 'visible'
};
Best Practices
- Use
unknowninstead ofanyfor flexible but safe types - Prefer type inference when the type is obvious
- Add explicit annotations for public API return types
- Use literal types for finite sets of values