About us Guides Projects Contacts
Админка
please wait

TypeScript's type system is remarkably powerful, enabling patterns that catch bugs at compile time rather than at runtime. Moving beyond basic types to advanced patterns dramatically improves code quality and developer experience. This guide covers TypeScript patterns every senior developer should master.

Why Advanced TypeScript

Mastering advanced TypeScript enables:

  1. Compile-Time Safety: Catch errors before they reach production
  2. Better IntelliSense: IDE knows your code's shape
  3. Self-Documenting Code: Types describe intent
  4. Refactoring Confidence: Type checker catches breaking changes
  5. API Design: Express constraints in the type system

Utility Types

Built-in Utility Types

interface User {
id: number;
name: string;
email: string;
password: string;
createdAt: Date;
}
// Partial: All properties optional
type UserUpdate = Partial<User>;
// { id?: number; name?: string; email?: string; ... }
// Required: All properties required
type CompleteUser = Required<Partial<User>>;
// Pick: Select specific properties
type UserPreview = Pick<User, 'id' | 'name'>;
// { id: number; name: string; }
// Omit: Exclude specific properties
type UserWithoutPassword = Omit<User, 'password'>;
// { id: number; name: string; email: string; createdAt: Date; }
// Readonly: All properties readonly
type ImmutableUser = Readonly<User>;
// Cannot reassign properties
// Record: Create object type with keys and value type
type UserRoles = Record<string, 'admin' | 'user' | 'guest'>;
// { [key: string]: 'admin' | 'user' | 'guest' }
// Extract: Extract types from union
type StringOrNumber = string | number | boolean;
type OnlyStrings = Extract<StringOrNumber, string>;
// string
// Exclude: Remove types from union
type NotString = Exclude<StringOrNumber, string>;
// number | boolean
// NonNullable: Remove null and undefined
type MaybeString = string | null | undefined;
type DefinitelyString = NonNullable<MaybeString>;
// string
// ReturnType: Get function return type
function createUser() {
return { id: 1, name: 'John' };
}
type NewUser = ReturnType<typeof createUser>;
// { id: number; name: string; }
// Parameters: Get function parameter types
function greet(name: string, age: number) {}
type GreetParams = Parameters<typeof greet>;
// [string, number]

Generics

Basic Generics

// Generic function
function identity<T>(value: T): T {
return value;
}
const num = identity(42); // number
const str = identity('hello'); // string
// Generic interface
interface Container<T> {
value: T;
getValue(): T;
}
// Generic class
class Box<T> {
constructor(private content: T) {}
getContent(): T {
return this.content;
}
}
const numberBox = new Box(123);
const stringBox = new Box('hello');

Constrained Generics

// Constraint with extends
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(item: T): void {
console.log(item.length);
}
logLength('hello'); // OK
logLength([1, 2, 3]); // OK
logLength({ length: 5 }); // OK
// logLength(123); // Error: number doesn't have length
// Multiple constraints
interface HasId {
id: number;
}
interface HasName {
name: string;
}
function merge<T extends HasId, U extends HasName>(a: T, b: U): T & U {
return { ...a, ...b };
}
// keyof constraint
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: 'John', age: 30 };
const name = getProperty(user, 'name'); // string
const age = getProperty(user, 'age'); // number
// getProperty(user, 'invalid'); // Error

Default Generic Types

interface ApiResponse<T = unknown> {
data: T;
status: number;
message: string;
}
// Uses default
const response: ApiResponse = { data: {}, status: 200, message: 'OK' };
// Explicit type
const userResponse: ApiResponse<User> = {
data: { id: 1, name: 'John', email: '[email protected]', password: '', createdAt: new Date() },
status: 200,
message: 'OK'
};

Conditional Types

Basic Conditional Types

// T extends U ? X : Y
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// Practical example: unwrap Promise
type Unwrap<T> = T extends Promise<infer U> ? U : T;
type PromiseString = Promise<string>;
type Unwrapped = Unwrap<PromiseString>; // string
type NotPromise = Unwrap<number>; // number
// Array element type
type ArrayElement<T> = T extends (infer E)[] ? E : never;
type StringArrayElement = ArrayElement<string[]>; // string

Distributive Conditional Types

type ToArray<T> = T extends any ? T[] : never;
// Distributes over union
type StringOrNumberArray = ToArray<string | number>;
// string[] | number[]
// Prevent distribution with []
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
type Combined = ToArrayNonDist<string | number>;
// (string | number)[]

Mapped Types

Basic Mapped Types

// Make all properties optional
type Optional<T> = {
[K in keyof T]?: T[K];
};
// Make all properties required
type Required<T> = {
[K in keyof T]-?: T[K];
};
// Make all properties readonly
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
// Remove readonly
type Mutable<T> = {
-readonly [K in keyof T]: T[K];
};

Advanced Mapped Types

// Transform property types
type Stringify<T> = {
[K in keyof T]: string;
};
// Filter properties by value type
type OnlyStrings<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K];
};
interface Mixed {
name: string;
age: number;
email: string;
}
type StringProps = OnlyStrings<Mixed>;
// { name: string; email: string; }
// Rename properties
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
interface Person {
name: string;
age: number;
}
type PersonGetters = Getters<Person>;
// { getName: () => string; getAge: () => number; }

Template Literal Types

Basic Template Literals

type Greeting = `Hello, ${string}!`;
const valid: Greeting = 'Hello, World!'; // OK
// const invalid: Greeting = 'Hi, World!'; // Error
// Combining unions
type Color = 'red' | 'green' | 'blue';
type Size = 'small' | 'medium' | 'large';
type ColoredSize = `${Color}-${Size}`;
// 'red-small' | 'red-medium' | 'red-large' | 'green-small' | ...
// HTTP methods
type Method = 'GET' | 'POST' | 'PUT' | 'DELETE';
type Endpoint = '/users' | '/posts';
type Route = `${Method} ${Endpoint}`;

Event Handler Types

type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<'click'>; // 'onClick'
type ChangeEvent = EventName<'change'>; // 'onChange'
// Create event handler type
type EventHandlers<T> = {
[K in keyof T as EventName<string & K>]: (value: T[K]) => void;
};
interface FormFields {
name: string;
email: string;
age: number;
}
type FormHandlers = EventHandlers<FormFields>;
// { onName: (value: string) => void; onEmail: (value: string) => void; onAge: (value: number) => void; }

Discriminated Unions

Pattern Matching

interface Loading {
status: 'loading';
}
interface Success<T> {
status: 'success';
data: T;
}
interface Error {
status: 'error';
error: string;
}
type AsyncState<T> = Loading | Success<T> | Error;
function handleState<T>(state: AsyncState<T>): string {
switch (state.status) {
case 'loading':
return 'Loading...';
case 'success':
return `Data: ${JSON.stringify(state.data)}`;
case 'error':
return `Error: ${state.error}`;
}
}
// Exhaustiveness checking
function assertNever(x: never): never {
throw new Error(`Unexpected value: ${x}`);
}
function handleStateExhaustive<T>(state: AsyncState<T>): string {
switch (state.status) {
case 'loading':
return 'Loading...';
case 'success':
return `Data: ${JSON.stringify(state.data)}`;
case 'error':
return `Error: ${state.error}`;
default:
return assertNever(state); // Compile error if case missing
}
}

Type Guards

Custom Type Guards

interface Cat {
meow(): void;
}
interface Dog {
bark(): void;
}
// Type predicate
function isCat(animal: Cat | Dog): animal is Cat {
return 'meow' in animal;
}
function makeSound(animal: Cat | Dog) {
if (isCat(animal)) {
animal.meow(); // TypeScript knows it's Cat
} else {
animal.bark(); // TypeScript knows it's Dog
}
}
// Assert function (throws if false)
function assertIsString(value: unknown): asserts value is string {
if (typeof value !== 'string') {
throw new Error('Value is not a string');
}
}
function processValue(value: unknown) {
assertIsString(value);
// TypeScript knows value is string here
console.log(value.toUpperCase());
}

Declaration Merging

Extending Existing Types

// Extend Window
declare global {
interface Window {
analytics: {
track: (event: string, data?: object) => void;
};
}
}
// Now valid
window.analytics.track('pageview');
// Extend Express Request
declare namespace Express {
interface Request {
user?: {
id: string;
role: string;
};
}
}
// Extend module
declare module 'express' {
interface Request {
customProperty: string;
}
}

Practical Patterns

Builder Pattern with Types

class RequestBuilder<T extends object = {}> {
private config: T = {} as T;
withUrl<U extends string>(url: U): RequestBuilder<T & { url: U }> {
return Object.assign(this, { config: { ...this.config, url } });
}
withMethod<M extends 'GET' | 'POST'>(method: M): RequestBuilder<T & { method: M }> {
return Object.assign(this, { config: { ...this.config, method } });
}
build(): T {
return this.config;
}
}
const request = new RequestBuilder()
.withUrl('/api/users')
.withMethod('GET')
.build();
// Type: { url: '/api/users'; method: 'GET'; }

Key Takeaways

  1. Use utility types: Don't reinvent Partial, Pick, Omit
  2. Constrain generics: extends makes types more useful
  3. Discriminated unions: Powerful pattern matching
  4. Type guards: Narrow types safely
  5. Template literals: Type-safe string manipulation
  6. Mapped types: Transform type shapes systematically

TypeScript's type system is a language within a language—invest in learning it deeply, and your code quality will improve dramatically.

 
 
 
Языки
Темы
Copyright © 1999 — 2026
ZK Interactive