Summary
TypeScript is a statically typed superset of JavaScript developed by Microsoft that adds optional static typing, classes, interfaces, and other features to JavaScript. It compiles to plain JavaScript and helps catch errors at compile-time rather than runtime, making code more maintainable and scalable. TypeScript's type system enables better IDE support with features like IntelliSense, refactoring tools, and auto-completion. It's particularly valuable in large-scale applications where type safety reduces bugs and improves developer productivity. With seamless integration with modern frameworks like React, Angular, and Vue, TypeScript has become the standard for enterprise JavaScript development. This cheatsheet covers TypeScript fundamentals, advanced type system features, and practical patterns for technical interviews.
1. TypeScript Basics
What is TypeScript?
- Superset of JavaScript that adds static typing
- Compiles to plain JavaScript
- Helps catch errors at compile-time
Basic Types
// Primitives
let isDone: boolean = false;
let decimal: number = 6;
let color: string = "blue";
let list: number[] = [1, 2, 3];
let tuple: [string, number] = ["hello", 10];
// Any & Unknown
let notSure: any = 4; // Avoid using 'any'
let uncertain: unknown = 4; // Safer than 'any'
// Void, Null, Undefined
function warnUser(): void {
console.log("Warning!");
}
let u: undefined = undefined;
let n: null = null;
Type Annotations & Inference
// Explicit type annotation
let myName: string = "Alice";
// Type inference
let myAge = 25; // TypeScript infers number
2. Interfaces & Types
Interface Declaration
interface User {
readonly id: number;
name: string;
age?: number; // Optional property
}
// Extending interfaces
interface Admin extends User {
role: string;
}
Type Aliases
type StringOrNumber = string | number;
type UserType = {
name: string;
age: number;
};
Interface vs Type
// Interface - can be extended & merged
interface Animal {
name: string;
}
interface Animal {
age: number; // Declaration merging
}
// Type - cannot be reopened
type Pet = {
name: string;
// Cannot add properties later
};
3. Functions
Function Types
// Function type annotation
function add(x: number, y: number): number {
return x + y;
}
// Arrow function
const multiply = (x: number, y: number): number => x * y;
// Optional & default parameters
function greet(name: string, greeting: string = "Hello"): string {
return `${greeting}, ${name}!`;
}
// Rest parameters
function sum(...numbers: number[]): number {
return numbers.reduce((a, b) => a + b, 0);
}
Function Overloading
function processInput(x: string): string;
function processInput(x: number): number;
function processInput(x: string | number): string | number {
if (typeof x === "string") {
return x.toUpperCase();
}
return x * 2;
}
4. Classes
Basic Class
class Person {
private name: string;
protected age: number;
public readonly id: number;
constructor(name: string, age: number, id: number) {
this.name = name;
this.age = age;
this.id = id;
}
public getName(): string {
return this.name;
}
}
// Inheritance
class Employee extends Person {
constructor(name: string, age: number, id: number, public salary: number) {
super(name, age, id);
}
}
Abstract Classes
abstract class Shape {
abstract getArea(): number;
printArea(): void {
console.log(this.getArea());
}
}
class Circle extends Shape {
constructor(private radius: number) {
super();
}
getArea(): number {
return Math.PI * this.radius ** 2;
}
}
5. Generics
Generic Functions
function identity<T>(arg: T): T {
return arg;
}
// Usage
let output1 = identity<string>("myString");
let output2 = identity<number>(100);
Generic Interfaces & Classes
interface Container<T> {
value: T;
}
class Box<T> {
constructor(private value: T) {}
getValue(): T {
return this.value;
}
}
Generic Constraints
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(arg: T): T {
console.log(arg.length);
return arg;
}
6. Advanced Types
Union Types
type Status = "pending" | "approved" | "rejected";
let currentStatus: Status = "pending";
function formatInput(input: string | number): string {
if (typeof input === "string") {
return input.toUpperCase();
}
return input.toFixed(2);
}
Intersection Types
type Draggable = {
drag: () => void;
};
type Resizable = {
resize: () => void;
};
type UIElement = Draggable & Resizable;
Type Guards
// typeof guard
function processValue(value: string | number) {
if (typeof value === "string") {
return value.toUpperCase();
}
return value.toFixed(2);
}
// instanceof guard
class Cat {
meow() {}
}
class Dog {
bark() {}
}
function makeSound(pet: Cat | Dog) {
if (pet instanceof Cat) {
pet.meow();
} else {
pet.bark();
}
}
// Custom type guard
function isString(value: any): value is string {
return typeof value === "string";
}
Mapped Types
// Make all properties optional
type Partial<T> = {
[P in keyof T]?: T[P];
};
// Make all properties readonly
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
// Pick specific properties
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
Conditional Types
type IsString<T> = T extends string ? true : false;
type ExtractArrayType<T> = T extends (infer U)[] ? U : T;
// Usage: ExtractArrayType<string[]> = string
7. Utility Types
Common Built-in Utility Types
interface Todo {
title: string;
description: string;
completed: boolean;
}
// Partial - makes all properties optional
type PartialTodo = Partial<Todo>;
// Required - makes all properties required
type RequiredTodo = Required<PartialTodo>;
// Pick - creates type with subset of properties
type TodoPreview = Pick<Todo, "title" | "completed">;
// Omit - creates type excluding properties
type TodoInfo = Omit<Todo, "completed">;
// Record - creates object type with specified keys/values
type PageInfo = Record<"home" | "about", { title: string }>;
// ReturnType - extracts return type of function
function getUser() {
return { name: "John", age: 30 };
}
type User = ReturnType<typeof getUser>;
8. Enums
Numeric & String Enums
// Numeric enum
enum Direction {
Up = 1,
Down,
Left,
Right
}
// String enum
enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE"
}
// Const enum (more efficient)
const enum Size {
Small = 1,
Medium = 2,
Large = 3
}
9. Modules
Export/Import
// math.ts
export function add(a: number, b: number): number {
return a + b;
}
export const PI = 3.14159;
export default class Calculator {
// ...
}
// main.ts
import Calculator, { add, PI } from './math';
import * as math from './math';
Namespaces
namespace Validation {
export interface StringValidator {
isValid(s: string): boolean;
}
export class EmailValidator implements StringValidator {
isValid(s: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s);
}
}
}
10. React with TypeScript
Functional Components
import React, { FC, useState } from 'react';
interface Props {
name: string;
age?: number;
onClick: (name: string) => void;
}
const UserCard: FC<Props> = ({ name, age = 0, onClick }) => {
const [count, setCount] = useState<number>(0);
return (
<div onClick={() => onClick(name)}>
<h1>{name}</h1>
<p>Age: {age}</p>
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
</div>
);
};
Hooks with TypeScript
// useState
const [user, setUser] = useState<User | null>(null);
// useReducer
interface State {
count: number;
}
type Action =
| { type: 'increment' }
| { type: 'decrement' }
| { type: 'reset'; payload: number };
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return { count: action.payload };
default:
return state;
}
}
// Custom Hook
function useCounter(initial: number): [number, () => void, () => void] {
const [count, setCount] = useState(initial);
const increment = () => setCount(c => c + 1);
const decrement = () => setCount(c => c - 1);
return [count, increment, decrement];
}
Event Handling
import { ChangeEvent, FormEvent, MouseEvent } from 'react';
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
};
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
// Handle form submission
};
const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
console.log(e.currentTarget.name);
};
11. Advanced Patterns
Discriminated Unions
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
type Shape = Square | Rectangle | Circle;
function getArea(shape: Shape): number {
switch (shape.kind) {
case "square":
return shape.size ** 2;
case "rectangle":
return shape.width * shape.height;
case "circle":
return Math.PI * shape.radius ** 2;
}
}
Template Literal Types
type EventName = "click" | "focus" | "blur";
type EventHandlerName = `on${Capitalize<EventName>}`;
// Results in: "onClick" | "onFocus" | "onBlur"
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
};
Decorators (Experimental)
function logged(target: any, key: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling ${key} with args:`, args);
return original.apply(this, args);
};
}
class Calculator {
@logged
add(a: number, b: number): number {
return a + b;
}
}
12. Type Assertions
Basic Assertions
// as syntax
let someValue: unknown = "this is a string";
let strLength: number = (someValue as string).length;
// angle-bracket syntax (not in JSX)
let strLength2: number = (<string>someValue).length;
// const assertions
const config = {
endpoint: "api/users",
timeout: 3000
} as const;
// Type is: { readonly endpoint: "api/users"; readonly timeout: 3000; }
13. Best Practices
1. Avoid any
// Bad
function process(data: any) { }
// Good
function process(data: unknown) {
if (typeof data === 'string') {
// Type is narrowed to string
}
}
2. Use Strict Mode
// tsconfig.json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true
}
}
3. Prefer Interfaces for Object Shapes
// Prefer
interface UserData {
name: string;
age: number;
}
// Over
type UserData = {
name: string;
age: number;
};
4. Use Type Guards
function isError(error: unknown): error is Error {
return error instanceof Error;
}
function handleError(error: unknown) {
if (isError(error)) {
console.log(error.message);
}
}
5. Leverage Type Inference
// Let TypeScript infer when obvious
const numbers = [1, 2, 3]; // number[]
// Be explicit when not obvious
const empty: string[] = [];
14. Key Interview Concepts
Interface vs Type Comparison
| Feature | Interface | Type Alias |
|---|---|---|
| Extending | extends keyword |
Intersection & |
| Merging | Declaration merging supported | Not supported |
| Computed Properties | Limited support | Full support |
| Union Types | Not directly | Supported |
| Primitives | Cannot represent | Can represent |
| Tuples | Can define | Better syntax |
| Use Case | Object shapes, contracts | Unions, complex types |
Type Safety Hierarchy
// From least to most safe
any // No type checking at all
unknown // Requires type checking before use
object // Non-primitive type
{[key: string]: any} // Index signature
{specific: types} // Specific object shape
literal types // Exact values only
Common Type Patterns
| Pattern | Use Case | Example |
|---|---|---|
| Discriminated Union | State machines, Redux | { type: 'success', data } | { type: 'error', error } |
| Branded Types | Validation, domain modeling | type UserId = string & { __brand: 'UserId' } |
| Exhaustive Check | Switch completeness | never type in default case |
| Builder Pattern | Fluent APIs | Method chaining with this return |
| Phantom Types | Compile-time constraints | Types that exist only at compile time |
15. TypeScript Configuration
Essential tsconfig.json Options
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020", "DOM"],
"jsx": "react",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
Quick Reference
Type Checking Commands
# Type check
tsc --noEmit
# Watch mode
tsc --watch
# Build
tsc
# With config
tsc -p tsconfig.json
Common Type Patterns
// Function that may not return
type MaybeReturns<T> = T | void;
// Nullable type
type Nullable<T> = T | null | undefined;
// Deep partial
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
// Promisify
type Promisify<T> = T extends Promise<any> ? T : Promise<T>;
Advanced Type System Features
Variance in TypeScript
// Covariance (outputs) - preserves subtyping
interface Producer<out T> {
produce(): T;
}
// Contravariance (inputs) - reverses subtyping
interface Consumer<in T> {
consume(item: T): void;
}
// Invariance (both) - exact type match required
interface Storage<T> {
get(): T;
set(item: T): void;
}
Type Narrowing Techniques
// 1. Truthiness narrowing
function print(str: string | null) {
if (str) {
console.log(str.toUpperCase()); // string
}
}
// 2. Equality narrowing
function compare(x: string | number, y: string | boolean) {
if (x === y) {
// x and y are both string here
}
}
// 3. in operator narrowing
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function move(animal: Fish | Bird) {
if ('swim' in animal) {
animal.swim(); // Fish
}
}
// 4. instanceof narrowing
if (error instanceof Error) {
console.log(error.message);
}
// 5. Type predicates
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
Recursive Types
// JSON type
type JSONValue =
| string
| number
| boolean
| null
| JSONObject
| JSONArray;
interface JSONObject {
[key: string]: JSONValue;
}
interface JSONArray extends Array<JSONValue> {}
// Nested object paths
type Path<T> = T extends object
? {
[K in keyof T]: K extends string
? `${K}` | `${K}.${Path<T[K]>}`
: never
}[keyof T]
: never;
Common TypeScript Gotchas
Excess Property Checks
// Direct assignment triggers excess property check
const obj: { a: number } = {
a: 1,
b: 2 // Error: Object literal may only specify known properties
};
// Indirect assignment bypasses check
const temp = { a: 1, b: 2 };
const obj2: { a: number } = temp; // OK
Index Signatures vs Record
// Index signature - flexible but less safe
interface StringDict {
[key: string]: string;
}
// Record - more type-safe for known keys
type ColorMap = Record<'red' | 'blue' | 'green', string>;
// Partial Record for optional keys
type PartialColorMap = Partial<Record<'red' | 'blue', string>>;
This Type in Classes
class Calculator {
value = 0;
add(n: number): this {
this.value += n;
return this;
}
multiply(n: number): this {
this.value *= n;
return this;
}
}
// Enables method chaining
new Calculator().add(5).multiply(2);
Performance Considerations
Type-Level Performance
// ❌ Expensive - deep recursion
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object
? DeepReadonly<T[P]>
: T[P];
};
// ✅ Better - limit recursion depth
type DeepReadonly<T, Depth extends number = 5> =
Depth extends 0 ? T :
T extends object ? {
readonly [P in keyof T]: DeepReadonly<T[P], /* Decrement Depth */>
} : T;
Compilation Performance Tips
- Use Project References for large codebases
- Enable
skipLibCheckto skip .d.ts checking - Prefer interfaces over intersections for object types
- Avoid excessive union types (>20 members)
- Use
const enumfor better runtime performance
TypeScript with Modern Frameworks
Vue 3 Composition API
import { defineComponent, ref, computed } from 'vue';
export default defineComponent({
props: {
initialCount: {
type: Number,
required: true
}
},
setup(props) {
const count = ref(props.initialCount);
const doubled = computed(() => count.value * 2);
function increment() {
count.value++;
}
return { count, doubled, increment };
}
});
Angular Strict Mode
@Component({
selector: 'app-user',
template: `{{ user?.name }}`
})
export class UserComponent {
@Input() user!: User; // Definite assignment assertion
@Output() userChange = new EventEmitter<User>();
updateUser(name: string): void {
this.userChange.emit({ ...this.user, name });
}
}
Interview Preparation Checklist
- ✅ Basic Types: Primitives, arrays, tuples, enums
- ✅ Type System: Interfaces, types, unions, intersections
- ✅ Generics: Constraints, variance, inference
- ✅ Advanced Types: Mapped, conditional, template literals
- ✅ Utility Types: Partial, Required, Pick, Omit, Record
- ✅ Type Guards: typeof, instanceof, custom predicates
- ✅ Strict Mode: Configuration and benefits
- ✅ React Integration: Props, hooks, events
- ✅ Best Practices: unknown vs any, type inference
- ✅ Performance: Compilation optimization, type complexity
Key Takeaway: TypeScript's type system provides powerful tools for building maintainable, scalable applications. Focus on understanding type inference, generics, and discriminated unions for most practical applications. Remember that TypeScript is a tool for developer productivity - use types to make invalid states unrepresentable and leverage the compiler to catch errors early.