Skip to content
Go back

The Ultimate Guide to Must-Know TypeScript Features

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.


Share this post on:

Previous Post
AOSS OpenSearch Serverless Development using AWS CDK
Next Post
Key Management Service in AWS