Skip to content

The Ultimate Guide to Must-Know TypeScript Features

Updated: at 10:00 AM

Table of content

tsconfig.json

We recommend starting with a well-configured tsconfig.json file to enable strict type checking and modern JavaScript features. Here’s a basic setup:

{
  "compilerOptions": {
    "target": "ES2020", // Modern JavaScript features
    "module": "ESNext", // Use ES modules
    "strict": true, // Enable all strict type-checking options
    "noImplicitAny": true, // Disallow implicit 'any' types
    "strictNullChecks": true, // Ensure null and undefined are handled explicitly
    "esModuleInterop": true, // Enable compatibility with CommonJS modules
    "skipLibCheck": true, // Skip type checking of declaration files
    "forceConsistentCasingInFileNames": true // Ensure file names are case-sensitive
  },
  "include": ["src/**/*"], // Include all TypeScript files in src directory
  "exclude": ["node_modules", "**/*.spec.ts"] // Exclude node_modules and test files
}

Core Language Features

Type Inference

Let TypeScript infer types wherever possible. It reduces noise and keeps the codebase clean.

const name = "Alice"; // inferred as string

Type Annotations (for function boundaries)

Always type function parameters and return values explicitly for clarity and contract enforcement.

function greet(name: string): string {
  return `Hello, ${name}`;
}

Union & Intersection Types

type Status = "draft" | "published"; // Union: can be either
type Article = Text & Metadata; // Intersection: must satisfy both

Union types are used when a value can be one of several types.
Intersection types combine multiple types into one with all their properties.


Type Aliases vs. Interfaces

type User = { id: number; name: string };
interface Admin {
  id: number;
  role: string;
}

type is more flexible — supports primitives, unions, and intersections.
interface is better for extending objects and class contracts.


Literal Types

type Color = "red" | "green" | "blue";

Restrict a value to exact strings, numbers, or booleans.


as const

const roles = ["admin", "user"] as const;
// typeof roles[number] → 'admin' | 'user'

Marks arrays and objects as deeply immutable and narrows literal types.


Utility Types

Partial<T>

Makes all properties in a type optional.

type User = { id: string; name: string };
Partial<User>; // { id?: string; name?: string }

Required<T>

Opposite of Partial — makes all properties required.

type Person = { name?: string; age?: number };
type RequiredPerson = Required<Person>; // { name: string; age: number }

Readonly<T>

Creates a type with all properties set to readonly.

type Config = { port: number };
const cfg: Readonly<Config> = { port: 3000 };
// cfg.port = 4000; ❌ Error: cannot assign to readonly property

Record<K, T>

Constructs an object type with a set of keys and uniform value type.

Record<"admin" | "user", boolean>; // { admin: boolean; user: boolean }

Pick<T, K>

Select specific properties from a type.

Pick<User, "id" | "name">;

Omit<T, K>

Exclude specific properties from a type.

Omit<User, "name">;

Exclude<T, U>

Exclude members from a union type.

Exclude<"a" | "b" | "c", "a">; // 'b' | 'c'

Extract<T, U>

Extract members of type T that are assignable to U.

type Events = "click" | "scroll" | "keydown";
type MouseEvents = Extract<Events, "click" | "scroll">; // 'click' | 'scroll'

NonNullable<T>

Removes null and undefined from a type.

type MaybeString = string | null | undefined;
type JustString = NonNullable<MaybeString>; // string

ReturnType<T> / Parameters<T>

Get type information from functions and constructors.

function getUser() {
  return { id: 1, name: "Alice" };
}
type User = ReturnType<typeof getUser>; // { id: number; name: string }
function greet(name: string, age: number) {}
type GreetArgs = Parameters<typeof greet>; // [string, number]

ConstructorParameters<T>

Gets the parameters of a class constructor.

class Person {
  constructor(
    public name: string,
    public age: number
  ) {}
}
type PersonArgs = ConstructorParameters<typeof Person>;
// [string, number]

Advanced Features

keyof

Get all property names of a type as a union of string literals.

type UserKeys = keyof User; // "id" | "name"

typeof

Use the type of a variable or constant.

const config = { host: "localhost", port: 3000 };
type Config = typeof config;

infer (inside conditional types)

Extract and reuse a type within a conditional.

type Unwrap<T> = T extends Promise<infer U> ? U : T;

Mapped Types

type Optional<T> = { [K in keyof T]?: T[K] };

Allows you to apply transformations across properties of a type.


Template Literal Types

type Lang = `lang-${"en" | "fr"}`; // 'lang-en' | 'lang-fr'

Conditional Types

type IsString<T> = T extends string ? true : false;

Enables dynamic type evaluation.


Discriminated Unions

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; size: number };

Enables safe narrowing using the kind field.


satisfies operator

type Config = {
  port: number;
  mode: "dev" | "prod";
};

// WRONG: TypeScript widens cfg.mode to string
const cfg = {
  port: 3000,
  mode: "dev",
}; // Type: { port: number; mode: string }
const typed: Config = cfg; // but now cfg.mode is widened to `string`

// CORRECT: Use satisfies to ensure type safety without widening
const cfg = {
  port: 3000,
  mode: "dev",
} satisfies Config;

Practical Patterns

Safe Object Access

function get<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

Prevents unsafe access with invalid keys.


Deep Readonly

type DeepReadonly<T> = {
  readonly [P in keyof T]: DeepReadonly<T[P]>;
};

Recursively makes all fields immutable.


Narrowing unknown safely

function isString(x: unknown): x is string {
  return typeof x === "string";
}

Use type guards to safely handle unknown.


Testing & Tooling

Enable strict mode

"strict": true

Enables full type safety features in your project.


Use tsc --noEmit for type-only validation

Compile code purely to check for type errors, without emitting JS output.



Anti-patterns to Avoid

Underrated Helpers

Wrap-up

TypeScript is a powerful type system that can model real-world data accurately and safely. Mastering these features will not only make your code cleaner and safer but also easier to maintain in the long run.

If you enjoy the content, please consider supporting work