Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:22:35 +08:00
commit 6fcffca9b0
35 changed files with 8235 additions and 0 deletions

View File

@@ -0,0 +1,168 @@
# Advanced Generic Patterns
## Recursive Generics
Use recursive generics to apply transformations deeply through nested object structures:
```typescript
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object
? DeepReadonly<T[P]>
: T[P];
};
interface Config {
database: {
host: string;
credentials: {
username: string;
password: string;
};
};
}
const config: DeepReadonly<Config> = {
database: {
host: "localhost",
credentials: {
username: "admin",
password: "secret"
}
}
};
```
## Variadic Tuple Types
TypeScript 4.0+ supports variadic tuple types for precise array concatenation:
```typescript
function concat<T extends readonly unknown[], U extends readonly unknown[]>(
arr1: T,
arr2: U
): [...T, ...U] {
return [...arr1, ...arr2];
}
const result = concat([1, 2], ["a", "b"]);
function curry<T extends unknown[], U extends unknown[], R>(
fn: (...args: [...T, ...U]) => R,
...first: T
): (...args: U) => R {
return (...rest: U) => fn(...first, ...rest);
}
```
## Template Literal Types
Combine string literals with generics for type-safe string manipulation:
```typescript
type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<"click">;
type HoverEvent = EventName<"hover">;
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type Endpoint<M extends HTTPMethod, P extends string> = `${M} ${P}`;
type UserEndpoint = Endpoint<"GET", "/users/:id">;
```
## Branded Types
Create nominal types in TypeScript's structural type system:
```typescript
type Brand<K, T> = K & { __brand: T };
type UserId = Brand<string, "UserId">;
type EmailAddress = Brand<string, "Email">;
type OrderId = Brand<string, "OrderId">;
function sendEmail(to: EmailAddress, from: EmailAddress): void {
console.log(`Sending email from ${from} to ${to}`);
}
function getUser(id: UserId): void {
console.log(`Fetching user ${id}`);
}
const email = "user@example.com" as EmailAddress;
const userId = "user-123" as UserId;
sendEmail(email, email);
getUser(userId);
```
## Distributive Conditional Types
Conditional types distribute over unions when the checked type is a naked type parameter:
```typescript
type ToArray<T> = T extends any ? T[] : never;
type Nums = ToArray<number | string>;
type NonNullable<T> = T extends null | undefined ? never : T;
type SafeString = NonNullable<string | null | undefined>;
```
## Mapped Type Modifiers
Use `+`, `-`, `readonly`, and `?` modifiers in mapped types:
```typescript
type Mutable<T> = {
-readonly [P in keyof T]: T[P];
};
type Required<T> = {
[P in keyof T]-?: T[P];
};
type ReadonlyPartial<T> = {
+readonly [P in keyof T]+?: T[P];
};
```
## Inference in Conditional Types
Use `infer` to extract types within conditional types:
```typescript
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type ArrayElement<T> = T extends (infer E)[] ? E : never;
type PromiseValue<T> = T extends Promise<infer V> ? V : T;
type FirstParameter<T> = T extends (first: infer F, ...args: any[]) => any
? F
: never;
```
## Higher-Kinded Types (Simulation)
While TypeScript doesn't have true HKTs, you can simulate them:
```typescript
interface Functor<F> {
map<A, B>(fa: HKT<F, A>, f: (a: A) => B): HKT<F, B>;
}
interface HKT<F, A> {
_F: F;
_A: A;
}
interface ArrayF {}
type ArrayHKT<A> = HKT<ArrayF, A> & A[];
const arrayFunctor: Functor<ArrayF> = {
map: (fa, f) => fa.map(f)
};
```

View File

@@ -0,0 +1,332 @@
# Common Generic Patterns
## Array Operations
Generic array utilities maintain type safety while providing reusable functionality:
```typescript
function last<T>(arr: T[]): T | undefined {
return arr[arr.length - 1];
}
function first<T>(arr: T[]): T | undefined {
return arr[0];
}
function chunk<T>(arr: T[], size: number): T[][] {
const chunks: T[][] = [];
for (let i = 0; i < arr.length; i += size) {
chunks.push(arr.slice(i, i + size));
}
return chunks;
}
function flatten<T>(arr: T[][]): T[] {
return arr.reduce((acc, item) => acc.concat(item), []);
}
function unique<T>(arr: T[]): T[] {
return Array.from(new Set(arr));
}
function partition<T>(arr: T[], predicate: (item: T) => boolean): [T[], T[]] {
const truthy: T[] = [];
const falsy: T[] = [];
for (const item of arr) {
if (predicate(item)) {
truthy.push(item);
} else {
falsy.push(item);
}
}
return [truthy, falsy];
}
```
## Object Utilities
Type-safe object manipulation:
```typescript
function pick<T extends object, K extends keyof T>(
obj: T,
...keys: K[]
): Pick<T, K> {
const result = {} as Pick<T, K>;
for (const key of keys) {
result[key] = obj[key];
}
return result;
}
function omit<T extends object, K extends keyof T>(
obj: T,
...keys: K[]
): Omit<T, K> {
const result = { ...obj };
for (const key of keys) {
delete result[key];
}
return result as Omit<T, K>;
}
function keys<T extends object>(obj: T): (keyof T)[] {
return Object.keys(obj) as (keyof T)[];
}
function values<T extends object>(obj: T): T[keyof T][] {
return Object.values(obj) as T[keyof T][];
}
function entries<T extends object>(obj: T): [keyof T, T[keyof T]][] {
return Object.entries(obj) as [keyof T, T[keyof T]][];
}
function mapValues<T extends object, U>(
obj: T,
fn: (value: T[keyof T], key: keyof T) => U
): Record<keyof T, U> {
const result = {} as Record<keyof T, U>;
for (const key in obj) {
result[key] = fn(obj[key], key);
}
return result;
}
```
## Promise Utilities
Generic async operations:
```typescript
async function retry<T>(
fn: () => Promise<T>,
maxAttempts: number
): Promise<T> {
let lastError: Error;
for (let i = 0; i < maxAttempts; i++) {
try {
return await fn();
} catch (error) {
lastError = error as Error;
}
}
throw lastError!;
}
async function timeout<T>(
promise: Promise<T>,
ms: number
): Promise<T> {
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => reject(new Error("Timeout")), ms);
});
return Promise.race([promise, timeoutPromise]);
}
async function sequence<T>(
promises: (() => Promise<T>)[]
): Promise<T[]> {
const results: T[] = [];
for (const promiseFn of promises) {
results.push(await promiseFn());
}
return results;
}
```
## Class Generics
Generic classes for reusable data structures:
```typescript
class Container<T> {
constructor(private value: T) {}
getValue(): T {
return this.value;
}
setValue(value: T): void {
this.value = value;
}
map<U>(fn: (value: T) => U): Container<U> {
return new Container(fn(this.value));
}
flatMap<U>(fn: (value: T) => Container<U>): Container<U> {
return fn(this.value);
}
}
class Result<T, E = Error> {
private constructor(
private readonly value?: T,
private readonly error?: E
) {}
static ok<T, E = Error>(value: T): Result<T, E> {
return new Result(value, undefined);
}
static err<T, E = Error>(error: E): Result<T, E> {
return new Result(undefined, error);
}
isOk(): this is Result<T, never> {
return this.value !== undefined;
}
isErr(): this is Result<never, E> {
return this.error !== undefined;
}
unwrap(): T {
if (this.error !== undefined) {
throw this.error;
}
return this.value!;
}
map<U>(fn: (value: T) => U): Result<U, E> {
if (this.error !== undefined) {
return Result.err(this.error);
}
return Result.ok(fn(this.value!));
}
}
```
## Builder Pattern
Fluent API with type safety:
```typescript
class QueryBuilder<T extends object> {
private filters: Array<(item: T) => boolean> = [];
private sortFn?: (a: T, b: T) => number;
private limitValue?: number;
where<K extends keyof T>(key: K, value: T[K]): this {
this.filters.push(item => item[key] === value);
return this;
}
whereFn(predicate: (item: T) => boolean): this {
this.filters.push(predicate);
return this;
}
sort<K extends keyof T>(key: K, direction: "asc" | "desc" = "asc"): this {
this.sortFn = (a, b) => {
const aVal = a[key];
const bVal = b[key];
if (aVal < bVal) return direction === "asc" ? -1 : 1;
if (aVal > bVal) return direction === "asc" ? 1 : -1;
return 0;
};
return this;
}
limit(n: number): this {
this.limitValue = n;
return this;
}
execute(data: T[]): T[] {
let result = data.filter(item =>
this.filters.every(filter => filter(item))
);
if (this.sortFn) {
result = result.sort(this.sortFn);
}
if (this.limitValue !== undefined) {
result = result.slice(0, this.limitValue);
}
return result;
}
}
const users = [
{ id: 1, name: "Alice", active: true, age: 30 },
{ id: 2, name: "Bob", active: false, age: 25 },
{ id: 3, name: "Charlie", active: true, age: 35 }
];
const active = new QueryBuilder<typeof users[0]>()
.where("active", true)
.sort("age", "desc")
.limit(5)
.execute(users);
```
## Event Emitter Pattern
Type-safe event handling:
```typescript
type EventMap = Record<string, any>;
class TypedEventEmitter<Events extends EventMap> {
private listeners: {
[K in keyof Events]?: Array<(data: Events[K]) => void>;
} = {};
on<K extends keyof Events>(
event: K,
listener: (data: Events[K]) => void
): this {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event]!.push(listener);
return this;
}
emit<K extends keyof Events>(event: K, data: Events[K]): void {
const listeners = this.listeners[event];
if (listeners) {
for (const listener of listeners) {
listener(data);
}
}
}
off<K extends keyof Events>(
event: K,
listener: (data: Events[K]) => void
): this {
const listeners = this.listeners[event];
if (listeners) {
this.listeners[event] = listeners.filter(l => l !== listener);
}
return this;
}
}
interface AppEvents {
userLogin: { userId: string; timestamp: number };
userLogout: { userId: string };
dataUpdated: { id: string; changes: Record<string, unknown> };
}
const emitter = new TypedEventEmitter<AppEvents>();
emitter.on("userLogin", (data) => {
console.log(`User ${data.userId} logged in at ${data.timestamp}`);
});
```

View File

@@ -0,0 +1,305 @@
# Detailed Generic Examples
## Deep Partial Implementation
The `DeepPartial` type recursively makes all properties optional, including nested objects:
```typescript
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
interface DatabaseConfig {
host: string;
port: number;
credentials: {
username: string;
password: string;
ssl: {
enabled: boolean;
cert: string;
};
};
}
const partialConfig: DeepPartial<DatabaseConfig> = {
host: "localhost",
credentials: {
username: "admin",
ssl: {
enabled: true
}
}
};
function mergeConfig(
defaults: DatabaseConfig,
override: DeepPartial<DatabaseConfig>
): DatabaseConfig {
return {
...defaults,
...override,
credentials: {
...defaults.credentials,
...override.credentials,
ssl: {
...defaults.credentials.ssl,
...override.credentials?.ssl
}
}
};
}
```
## Conditional Return Types
Use conditional types to change return types based on input:
```typescript
type ParseResult<T extends "json" | "text"> =
T extends "json" ? unknown : string;
function parse<T extends "json" | "text">(
response: Response,
type: T
): Promise<ParseResult<T>> {
if (type === "json") {
return response.json() as Promise<ParseResult<T>>;
}
return response.text() as Promise<ParseResult<T>>;
}
async function example() {
const json = await parse(response, "json");
const text = await parse(response, "text");
}
type QueryResult<T extends boolean> = T extends true
? Array<{ id: string; data: unknown }>
: { id: string; data: unknown } | undefined;
function query<Multiple extends boolean = false>(
sql: string,
multiple?: Multiple
): QueryResult<Multiple> {
if (multiple) {
return [] as QueryResult<Multiple>;
}
return undefined as QueryResult<Multiple>;
}
const single = query("SELECT * FROM users WHERE id = 1");
const many = query("SELECT * FROM users", true);
```
## Filter by Type Pattern
Extract only properties of a specific type from an object:
```typescript
type FilterByType<T, U> = {
[P in keyof T as T[P] extends U ? P : never]: T[P];
};
interface User {
id: string;
name: string;
age: number;
active: boolean;
createdAt: Date;
score: number;
tags: string[];
}
type StringProps = FilterByType<User, string>;
type NumberProps = FilterByType<User, number>;
type FilterByValueType<T, U> = Pick<
T,
{
[K in keyof T]: T[K] extends U ? K : never;
}[keyof T]
>;
type BooleanProps = FilterByValueType<User, boolean>;
```
## Unwrap Nested Promises
Extract the final resolved type from nested Promises:
```typescript
type Awaited<T> = T extends Promise<infer U>
? Awaited<U>
: T;
type A = Awaited<Promise<string>>;
type B = Awaited<Promise<Promise<number>>>;
type C = Awaited<Promise<Promise<Promise<boolean>>>>;
async function deepFetch(): Promise<Promise<{ data: string }>> {
return Promise.resolve(
Promise.resolve({ data: "nested" })
);
}
type DeepFetchResult = Awaited<ReturnType<typeof deepFetch>>;
```
## Extract Function Parameters
Get parameter types from function signatures:
```typescript
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
function processUser(id: string, name: string, age: number): void {
console.log(id, name, age);
}
type ProcessUserParams = Parameters<typeof processUser>;
function callWithParams<F extends (...args: any[]) => any>(
fn: F,
...args: Parameters<F>
): ReturnType<F> {
return fn(...args);
}
callWithParams(processUser, "123", "Alice", 30);
type FirstParameter<T> = T extends (first: infer F, ...args: any[]) => any
? F
: never;
type FirstParam = FirstParameter<typeof processUser>;
```
## Constructable Types
Work with classes and constructors generically:
```typescript
interface Constructable<T> {
new (...args: any[]): T;
}
function create<T>(Constructor: Constructable<T>): T {
return new Constructor();
}
function createWithArgs<T, Args extends any[]>(
Constructor: new (...args: Args) => T,
...args: Args
): T {
return new Constructor(...args);
}
class User {
constructor(public name: string, public age: number) {}
}
const user1 = create(User);
const user2 = createWithArgs(User, "Alice", 30);
function inject<T>(
Constructor: Constructable<T>,
dependencies: Map<Constructable<any>, any>
): T {
const args = getDependencies(Constructor, dependencies);
return new Constructor(...args);
}
```
## Mapped Type Transformations
Complex property transformations using mapped types:
```typescript
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
interface Person {
name: string;
age: number;
}
type PersonGetters = Getters<Person>;
type Setters<T> = {
[K in keyof T as `set${Capitalize<string & K>}`]: (value: T[K]) => void;
};
type PersonSetters = Setters<Person>;
type ReadonlyKeys<T> = {
[K in keyof T]-?: T[K] extends { readonly [key: string]: any }
? K
: never;
}[keyof T];
type WritableKeys<T> = {
[K in keyof T]-?: T[K] extends { readonly [key: string]: any }
? never
: K;
}[keyof T];
```
## Type-Safe Event Handlers
Generic event handler system with strict typing:
```typescript
interface EventHandlers<T> {
onCreate?: (item: T) => void;
onUpdate?: (item: T, changes: Partial<T>) => void;
onDelete?: (id: string) => void;
}
class Store<T extends { id: string }> {
private items = new Map<string, T>();
private handlers: EventHandlers<T> = {};
setHandlers(handlers: EventHandlers<T>): void {
this.handlers = handlers;
}
create(item: T): void {
this.items.set(item.id, item);
this.handlers.onCreate?.(item);
}
update(id: string, changes: Partial<T>): void {
const item = this.items.get(id);
if (item) {
const updated = { ...item, ...changes };
this.items.set(id, updated);
this.handlers.onUpdate?.(updated, changes);
}
}
delete(id: string): void {
this.items.delete(id);
this.handlers.onDelete?.(id);
}
}
interface Todo {
id: string;
title: string;
completed: boolean;
}
const todoStore = new Store<Todo>();
todoStore.setHandlers({
onCreate: (todo) => console.log("Created:", todo.title),
onUpdate: (todo, changes) => console.log("Updated:", changes),
onDelete: (id) => console.log("Deleted:", id)
});
```