home / skills / doanchienthangdev / omgkit / typescript
This skill helps you master TypeScript type safety and generics to build scalable, maintainable code with confidence.
npx playbooks add skill doanchienthangdev/omgkit --skill typescriptReview the files below or copy the command above to add this skill to your agents.
---
name: typescript
description: TypeScript development with advanced type safety, generics, utility types, and best practices
category: languages
triggers:
- typescript
- ts
- type
- interface
- generic
- type safety
---
# TypeScript
Enterprise-grade **TypeScript development** following industry best practices. This skill covers type system mastery, advanced patterns, generic programming, utility types, and type-safe design patterns used by top engineering teams.
## Purpose
Write type-safe code that scales with confidence:
- Master TypeScript's type system
- Leverage generics for reusable code
- Use utility types effectively
- Implement type-safe patterns
- Handle complex type scenarios
- Improve code maintainability
## Features
### 1. Type Fundamentals
```typescript
// Primitive types
const name: string = 'John';
const age: number = 30;
const isActive: boolean = true;
// Arrays and tuples
const numbers: number[] = [1, 2, 3];
const tuple: [string, number, boolean] = ['John', 30, true];
const namedTuple: [name: string, age: number] = ['John', 30];
// Object types
interface User {
id: string;
name: string;
email: string;
age?: number; // Optional
readonly createdAt: Date; // Readonly
}
// Type aliases
type ID = string | number;
type Status = 'pending' | 'active' | 'inactive';
type Callback = (error: Error | null, result?: unknown) => void;
// Union and intersection types
type StringOrNumber = string | number;
type AdminUser = User & { role: 'admin'; permissions: string[] };
// Literal types
type Direction = 'north' | 'south' | 'east' | 'west';
type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
// Template literal types
type EventName = `on${Capitalize<string>}`;
type APIRoute = `/api/${string}`;
```
### 2. Advanced Type Patterns
```typescript
// Discriminated unions
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
function processResult<T>(result: Result<T>): T | null {
if (result.success) {
return result.data;
} else {
console.error(result.error);
return null;
}
}
// Type guards
function isString(value: unknown): value is string {
return typeof value === 'string';
}
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'email' in value
);
}
// Assertion functions
function assertDefined<T>(value: T | null | undefined): asserts value is T {
if (value === null || value === undefined) {
throw new Error('Value is not defined');
}
}
// Conditional types
type NonNullable<T> = T extends null | undefined ? never : T;
type ExtractArray<T> = T extends (infer U)[] ? U : never;
type ReturnTypeOf<T> = T extends (...args: any[]) => infer R ? R : never;
// Mapped types
type Nullable<T> = { [K in keyof T]: T[K] | null };
type Optional<T> = { [K in keyof T]?: T[K] };
type Readonly<T> = { readonly [K in keyof T]: T[K] };
```
### 3. Generics
```typescript
// Generic functions
function identity<T>(value: T): T {
return value;
}
function first<T>(array: T[]): T | undefined {
return array[0];
}
function map<T, U>(array: T[], fn: (item: T) => U): U[] {
return array.map(fn);
}
// Generic constraints
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(value: T): T {
console.log(value.length);
return value;
}
// Generic interfaces
interface Repository<T, ID = string> {
findById(id: ID): Promise<T | null>;
findAll(): Promise<T[]>;
create(data: Omit<T, 'id'>): Promise<T>;
update(id: ID, data: Partial<T>): Promise<T | null>;
delete(id: ID): Promise<boolean>;
}
// Generic classes
class Stack<T> {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
peek(): T | undefined {
return this.items[this.items.length - 1];
}
}
// Constrained generic with keyof
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
```
### 4. Utility Types
```typescript
interface User {
id: string;
name: string;
email: string;
password: string;
role: 'user' | 'admin';
createdAt: Date;
}
// Partial - make all optional
type UserUpdate = Partial<User>;
// Required - make all required
type CompleteUser = Required<User>;
// Readonly - make all readonly
type ImmutableUser = Readonly<User>;
// Pick - select specific properties
type UserCredentials = Pick<User, 'email' | 'password'>;
// Omit - exclude specific properties
type PublicUser = Omit<User, 'password'>;
// Record - create object type
type UserRoles = Record<string, User['role']>;
// Exclude/Extract - work with unions
type NonAdminRole = Exclude<User['role'], 'admin'>;
// Parameters/ReturnType - function types
type FunctionParams = Parameters<(a: string, b: number) => void>;
type FunctionReturn = ReturnType<() => Promise<User>>;
// Custom utility types
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};
```
### 5. Type-Safe API Design
```typescript
// Type-safe API client
interface APIEndpoints {
'/users': {
GET: { response: User[]; params: { page?: number } };
POST: { response: User; body: Omit<User, 'id'> };
};
'/users/:id': {
GET: { response: User; params: { id: string } };
DELETE: { response: void; params: { id: string } };
};
}
async function apiRequest<
Path extends keyof APIEndpoints,
Method extends keyof APIEndpoints[Path]
>(
path: Path,
method: Method,
config?: APIEndpoints[Path][Method]
): Promise<APIEndpoints[Path][Method]['response']> {
const response = await fetch(path, { method: method as string });
return response.json();
}
```
### 6. Error Handling Patterns
```typescript
// Type-safe Result type
type Result<T, E = Error> =
| { ok: true; value: T }
| { ok: false; error: E };
function ok<T>(value: T): Result<T, never> {
return { ok: true, value };
}
function err<E>(error: E): Result<never, E> {
return { ok: false, error };
}
// Error class hierarchy
class AppError extends Error {
constructor(
message: string,
public readonly code: string,
public readonly statusCode: number = 500
) {
super(message);
this.name = 'AppError';
}
}
class NotFoundError extends AppError {
constructor(resource: string, id: string) {
super(`${resource} with id ${id} not found`, 'NOT_FOUND', 404);
}
}
// Try/catch wrapper
async function tryCatch<T>(fn: () => Promise<T>): Promise<Result<T, Error>> {
try {
const value = await fn();
return ok(value);
} catch (error) {
return err(error instanceof Error ? error : new Error(String(error)));
}
}
```
### 7. Declaration Files
```typescript
// types/express.d.ts
declare global {
namespace Express {
interface Request {
user?: User;
requestId: string;
}
}
}
// types/environment.d.ts
declare global {
namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'production' | 'test';
DATABASE_URL: string;
API_KEY: string;
}
}
}
// Module declarations
declare module '*.svg' {
const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
export default content;
}
```
## Use Cases
### Type-Safe Redux State
```typescript
interface AppState {
users: UsersState;
products: ProductsState;
}
type Action =
| { type: 'USERS_LOADED'; payload: User[] }
| { type: 'USER_ADDED'; payload: User };
function reducer(state: AppState, action: Action): AppState {
switch (action.type) {
case 'USERS_LOADED':
return { ...state, users: { items: action.payload } };
default:
return state;
}
}
```
### Type-Safe Event Emitter
```typescript
type EventMap = {
userCreated: { user: User };
userDeleted: { userId: string };
};
class TypedEventEmitter<Events extends Record<string, unknown>> {
private listeners = new Map<keyof Events, Set<(data: any) => void>>();
on<E extends keyof Events>(event: E, listener: (data: Events[E]) => void): void {
if (!this.listeners.has(event)) {
this.listeners.set(event, new Set());
}
this.listeners.get(event)!.add(listener);
}
emit<E extends keyof Events>(event: E, data: Events[E]): void {
this.listeners.get(event)?.forEach(listener => listener(data));
}
}
```
## Best Practices
### Do's
- Enable strict mode in tsconfig
- Use interfaces for object shapes
- Leverage type inference
- Use discriminated unions for state
- Prefer readonly for immutable data
- Use unknown over any
- Create reusable generic types
### Don'ts
- Don't use any unless necessary
- Don't ignore errors with @ts-ignore
- Don't overuse type assertions
- Don't create overly complex generics
- Don't forget null/undefined handling
- Don't skip strict null checks
## Configuration
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}
```
## References
- [TypeScript Documentation](https://www.typescriptlang.org/docs)
- [TypeScript Deep Dive](https://basarat.gitbook.io/typescript)
- [Type Challenges](https://github.com/type-challenges/type-challenges)
This skill provides enterprise-grade TypeScript development guidance and patterns for building scalable, type-safe applications. It focuses on advanced type system features, generics, utility types, and practical design patterns used by professional teams. The content helps teams reduce runtime errors and improve maintainability through strong typing and best practices.
The skill inspects common TypeScript scenarios and presents concrete patterns: discriminated unions, type guards, conditional and mapped types, generics, and utility types. It shows how to design type-safe APIs, error-handling.Result patterns, and declaration files to extend runtime libraries with static types. Examples and small reusable primitives illustrate how to apply each concept in real codebases.
How do I start adding TypeScript to an existing JS project?
Enable incremental compilation, set strict:false initially, add allowJs and checkJs as needed, and migrate files one module at a time. Gradually enable stricter flags and introduce types for critical modules first.
When should I use interfaces vs type aliases?
Use interfaces for extendable object shapes and public contracts; use type aliases for unions, mapped/conditional types, or when composing complex type transforms.