Initial commit
This commit is contained in:
22
.claude-plugin/plugin.json
Normal file
22
.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "javascript-typescript",
|
||||
"description": "JavaScript and TypeScript development with ES6+, Node.js, React, and modern web frameworks",
|
||||
"version": "1.2.1",
|
||||
"author": {
|
||||
"name": "Seth Hobson",
|
||||
"url": "https://github.com/wshobson"
|
||||
},
|
||||
"skills": [
|
||||
"./skills/typescript-advanced-types",
|
||||
"./skills/nodejs-backend-patterns",
|
||||
"./skills/javascript-testing-patterns",
|
||||
"./skills/modern-javascript-patterns"
|
||||
],
|
||||
"agents": [
|
||||
"./agents/javascript-pro.md",
|
||||
"./agents/typescript-pro.md"
|
||||
],
|
||||
"commands": [
|
||||
"./commands/typescript-scaffold.md"
|
||||
]
|
||||
}
|
||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# javascript-typescript
|
||||
|
||||
JavaScript and TypeScript development with ES6+, Node.js, React, and modern web frameworks
|
||||
35
agents/javascript-pro.md
Normal file
35
agents/javascript-pro.md
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: javascript-pro
|
||||
description: Master modern JavaScript with ES6+, async patterns, and Node.js APIs. Handles promises, event loops, and browser/Node compatibility. Use PROACTIVELY for JavaScript optimization, async debugging, or complex JS patterns.
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
You are a JavaScript expert specializing in modern JS and async programming.
|
||||
|
||||
## Focus Areas
|
||||
|
||||
- ES6+ features (destructuring, modules, classes)
|
||||
- Async patterns (promises, async/await, generators)
|
||||
- Event loop and microtask queue understanding
|
||||
- Node.js APIs and performance optimization
|
||||
- Browser APIs and cross-browser compatibility
|
||||
- TypeScript migration and type safety
|
||||
|
||||
## Approach
|
||||
|
||||
1. Prefer async/await over promise chains
|
||||
2. Use functional patterns where appropriate
|
||||
3. Handle errors at appropriate boundaries
|
||||
4. Avoid callback hell with modern patterns
|
||||
5. Consider bundle size for browser code
|
||||
|
||||
## Output
|
||||
|
||||
- Modern JavaScript with proper error handling
|
||||
- Async code with race condition prevention
|
||||
- Module structure with clean exports
|
||||
- Jest tests with async test patterns
|
||||
- Performance profiling results
|
||||
- Polyfill strategy for browser compatibility
|
||||
|
||||
Support both Node.js and browser environments. Include JSDoc comments.
|
||||
33
agents/typescript-pro.md
Normal file
33
agents/typescript-pro.md
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: typescript-pro
|
||||
description: Master TypeScript with advanced types, generics, and strict type safety. Handles complex type systems, decorators, and enterprise-grade patterns. Use PROACTIVELY for TypeScript architecture, type inference optimization, or advanced typing patterns.
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
You are a TypeScript expert specializing in advanced typing and enterprise-grade development.
|
||||
|
||||
## Focus Areas
|
||||
- Advanced type systems (generics, conditional types, mapped types)
|
||||
- Strict TypeScript configuration and compiler options
|
||||
- Type inference optimization and utility types
|
||||
- Decorators and metadata programming
|
||||
- Module systems and namespace organization
|
||||
- Integration with modern frameworks (React, Node.js, Express)
|
||||
|
||||
## Approach
|
||||
1. Leverage strict type checking with appropriate compiler flags
|
||||
2. Use generics and utility types for maximum type safety
|
||||
3. Prefer type inference over explicit annotations when clear
|
||||
4. Design robust interfaces and abstract classes
|
||||
5. Implement proper error boundaries with typed exceptions
|
||||
6. Optimize build times with incremental compilation
|
||||
|
||||
## Output
|
||||
- Strongly-typed TypeScript with comprehensive interfaces
|
||||
- Generic functions and classes with proper constraints
|
||||
- Custom utility types and advanced type manipulations
|
||||
- Jest/Vitest tests with proper type assertions
|
||||
- TSConfig optimization for project requirements
|
||||
- Type declaration files (.d.ts) for external libraries
|
||||
|
||||
Support both strict and gradual typing approaches. Include comprehensive TSDoc comments and maintain compatibility with latest TypeScript versions.
|
||||
346
commands/typescript-scaffold.md
Normal file
346
commands/typescript-scaffold.md
Normal file
@@ -0,0 +1,346 @@
|
||||
# TypeScript Project Scaffolding
|
||||
|
||||
You are a TypeScript project architecture expert specializing in scaffolding production-ready Node.js and frontend applications. Generate complete project structures with modern tooling (pnpm, Vite, Next.js), type safety, testing setup, and configuration following current best practices.
|
||||
|
||||
## Context
|
||||
|
||||
The user needs automated TypeScript project scaffolding that creates consistent, type-safe applications with proper structure, dependency management, testing, and build tooling. Focus on modern TypeScript patterns and scalable architecture.
|
||||
|
||||
## Requirements
|
||||
|
||||
$ARGUMENTS
|
||||
|
||||
## Instructions
|
||||
|
||||
### 1. Analyze Project Type
|
||||
|
||||
Determine the project type from user requirements:
|
||||
- **Next.js**: Full-stack React applications, SSR/SSG, API routes
|
||||
- **React + Vite**: SPA applications, component libraries
|
||||
- **Node.js API**: Express/Fastify backends, microservices
|
||||
- **Library**: Reusable packages, utilities, tools
|
||||
- **CLI**: Command-line tools, automation scripts
|
||||
|
||||
### 2. Initialize Project with pnpm
|
||||
|
||||
```bash
|
||||
# Install pnpm if needed
|
||||
npm install -g pnpm
|
||||
|
||||
# Initialize project
|
||||
mkdir project-name && cd project-name
|
||||
pnpm init
|
||||
|
||||
# Initialize git
|
||||
git init
|
||||
echo "node_modules/" >> .gitignore
|
||||
echo "dist/" >> .gitignore
|
||||
echo ".env" >> .gitignore
|
||||
```
|
||||
|
||||
### 3. Generate Next.js Project Structure
|
||||
|
||||
```bash
|
||||
# Create Next.js project with TypeScript
|
||||
pnpm create next-app@latest . --typescript --tailwind --app --src-dir --import-alias "@/*"
|
||||
```
|
||||
|
||||
```
|
||||
nextjs-project/
|
||||
├── package.json
|
||||
├── tsconfig.json
|
||||
├── next.config.js
|
||||
├── .env.example
|
||||
├── src/
|
||||
│ ├── app/
|
||||
│ │ ├── layout.tsx
|
||||
│ │ ├── page.tsx
|
||||
│ │ ├── api/
|
||||
│ │ │ └── health/
|
||||
│ │ │ └── route.ts
|
||||
│ │ └── (routes)/
|
||||
│ │ └── dashboard/
|
||||
│ │ └── page.tsx
|
||||
│ ├── components/
|
||||
│ │ ├── ui/
|
||||
│ │ │ ├── Button.tsx
|
||||
│ │ │ └── Card.tsx
|
||||
│ │ └── layout/
|
||||
│ │ ├── Header.tsx
|
||||
│ │ └── Footer.tsx
|
||||
│ ├── lib/
|
||||
│ │ ├── api.ts
|
||||
│ │ ├── utils.ts
|
||||
│ │ └── types.ts
|
||||
│ └── hooks/
|
||||
│ ├── useAuth.ts
|
||||
│ └── useFetch.ts
|
||||
└── tests/
|
||||
├── setup.ts
|
||||
└── components/
|
||||
└── Button.test.tsx
|
||||
```
|
||||
|
||||
**package.json**:
|
||||
```json
|
||||
{
|
||||
"name": "nextjs-project",
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"test": "vitest",
|
||||
"type-check": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "^14.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.11.0",
|
||||
"@types/react": "^18.2.0",
|
||||
"typescript": "^5.3.0",
|
||||
"vitest": "^1.2.0",
|
||||
"@vitejs/plugin-react": "^4.2.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-next": "^14.1.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**tsconfig.json**:
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"jsx": "preserve",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"allowJs": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"incremental": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"plugins": [{"name": "next"}]
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Generate React + Vite Project Structure
|
||||
|
||||
```bash
|
||||
# Create Vite project
|
||||
pnpm create vite . --template react-ts
|
||||
```
|
||||
|
||||
**vite.config.ts**:
|
||||
```typescript
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import path from 'path'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
port: 3000,
|
||||
},
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
setupFiles: './tests/setup.ts',
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### 5. Generate Node.js API Project Structure
|
||||
|
||||
```
|
||||
nodejs-api/
|
||||
├── package.json
|
||||
├── tsconfig.json
|
||||
├── src/
|
||||
│ ├── index.ts
|
||||
│ ├── app.ts
|
||||
│ ├── config/
|
||||
│ │ ├── database.ts
|
||||
│ │ └── env.ts
|
||||
│ ├── routes/
|
||||
│ │ ├── index.ts
|
||||
│ │ ├── users.ts
|
||||
│ │ └── health.ts
|
||||
│ ├── controllers/
|
||||
│ │ └── userController.ts
|
||||
│ ├── services/
|
||||
│ │ └── userService.ts
|
||||
│ ├── models/
|
||||
│ │ └── User.ts
|
||||
│ ├── middleware/
|
||||
│ │ ├── auth.ts
|
||||
│ │ └── errorHandler.ts
|
||||
│ └── types/
|
||||
│ └── express.d.ts
|
||||
└── tests/
|
||||
└── routes/
|
||||
└── users.test.ts
|
||||
```
|
||||
|
||||
**package.json for Node.js API**:
|
||||
```json
|
||||
{
|
||||
"name": "nodejs-api",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "tsx watch src/index.ts",
|
||||
"build": "tsc",
|
||||
"start": "node dist/index.js",
|
||||
"test": "vitest",
|
||||
"lint": "eslint src --ext .ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^4.18.2",
|
||||
"dotenv": "^16.4.0",
|
||||
"zod": "^3.22.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/node": "^20.11.0",
|
||||
"typescript": "^5.3.0",
|
||||
"tsx": "^4.7.0",
|
||||
"vitest": "^1.2.0",
|
||||
"eslint": "^8.56.0",
|
||||
"@typescript-eslint/parser": "^6.19.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.19.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**src/app.ts**:
|
||||
```typescript
|
||||
import express, { Express } from 'express'
|
||||
import { healthRouter } from './routes/health.js'
|
||||
import { userRouter } from './routes/users.js'
|
||||
import { errorHandler } from './middleware/errorHandler.js'
|
||||
|
||||
export function createApp(): Express {
|
||||
const app = express()
|
||||
|
||||
app.use(express.json())
|
||||
app.use('/health', healthRouter)
|
||||
app.use('/api/users', userRouter)
|
||||
app.use(errorHandler)
|
||||
|
||||
return app
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Generate TypeScript Library Structure
|
||||
|
||||
```
|
||||
library-name/
|
||||
├── package.json
|
||||
├── tsconfig.json
|
||||
├── tsconfig.build.json
|
||||
├── src/
|
||||
│ ├── index.ts
|
||||
│ └── core.ts
|
||||
├── tests/
|
||||
│ └── core.test.ts
|
||||
└── dist/
|
||||
```
|
||||
|
||||
**package.json for Library**:
|
||||
```json
|
||||
{
|
||||
"name": "@scope/library-name",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
}
|
||||
},
|
||||
"files": ["dist"],
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.build.json",
|
||||
"test": "vitest",
|
||||
"prepublishOnly": "pnpm build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.3.0",
|
||||
"vitest": "^1.2.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Configure Development Tools
|
||||
|
||||
**.env.example**:
|
||||
```env
|
||||
NODE_ENV=development
|
||||
PORT=3000
|
||||
DATABASE_URL=postgresql://user:pass@localhost:5432/db
|
||||
JWT_SECRET=your-secret-key
|
||||
```
|
||||
|
||||
**vitest.config.ts**:
|
||||
```typescript
|
||||
import { defineConfig } from 'vitest/config'
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'node',
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
reporter: ['text', 'json', 'html'],
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
**.eslintrc.json**:
|
||||
```json
|
||||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
"@typescript-eslint/no-unused-vars": "error"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Output Format
|
||||
|
||||
1. **Project Structure**: Complete directory tree with all necessary files
|
||||
2. **Configuration**: package.json, tsconfig.json, build tooling
|
||||
3. **Entry Point**: Main application file with type-safe setup
|
||||
4. **Tests**: Test structure with Vitest configuration
|
||||
5. **Documentation**: README with setup and usage instructions
|
||||
6. **Development Tools**: .env.example, .gitignore, linting config
|
||||
|
||||
Focus on creating production-ready TypeScript projects with modern tooling, strict type safety, and comprehensive testing setup.
|
||||
69
plugin.lock.json
Normal file
69
plugin.lock.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"$schema": "internal://schemas/plugin.lock.v1.json",
|
||||
"pluginId": "gh:HermeticOrmus/Alqvimia-Contador:plugins/javascript-typescript",
|
||||
"normalized": {
|
||||
"repo": null,
|
||||
"ref": "refs/tags/v20251128.0",
|
||||
"commit": "52a0bd49608ce6390f563b3faa26ade6bdff14ea",
|
||||
"treeHash": "289e93e00b74761c3f14be346f07d0500cf0ff26959c5670036a790700303e36",
|
||||
"generatedAt": "2025-11-28T10:10:44.230711Z",
|
||||
"toolVersion": "publish_plugins.py@0.2.0"
|
||||
},
|
||||
"origin": {
|
||||
"remote": "git@github.com:zhongweili/42plugin-data.git",
|
||||
"branch": "master",
|
||||
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
|
||||
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
|
||||
},
|
||||
"manifest": {
|
||||
"name": "javascript-typescript",
|
||||
"description": "JavaScript and TypeScript development with ES6+, Node.js, React, and modern web frameworks",
|
||||
"version": "1.2.1"
|
||||
},
|
||||
"content": {
|
||||
"files": [
|
||||
{
|
||||
"path": "README.md",
|
||||
"sha256": "4bf011b5d64574c09cfd6c33eeffa76c82617d74d942216ee6035c76d8b3aadd"
|
||||
},
|
||||
{
|
||||
"path": "agents/javascript-pro.md",
|
||||
"sha256": "c8b65214462d98fa1f62a7ce26ea30341ca35f2c77313a18daa9553b0e4d5bed"
|
||||
},
|
||||
{
|
||||
"path": "agents/typescript-pro.md",
|
||||
"sha256": "8eb037a2a80b332807511960e87e3b50b57f76334b741674a7c71b4ea4840bb5"
|
||||
},
|
||||
{
|
||||
"path": ".claude-plugin/plugin.json",
|
||||
"sha256": "f23466cea34c79e8e79dbd7d6ededecf8cbc205c94f0fdeb1486e50a6419c3f8"
|
||||
},
|
||||
{
|
||||
"path": "commands/typescript-scaffold.md",
|
||||
"sha256": "b6fe21500713f7c049fc0a42e9c19cd522c1e4963dff1fc62952e90837e0be30"
|
||||
},
|
||||
{
|
||||
"path": "skills/javascript-testing-patterns/SKILL.md",
|
||||
"sha256": "91bd0860da653c2fc796b47be1e1f55c61d4e72ab0aaaf18509cfd2ca3805352"
|
||||
},
|
||||
{
|
||||
"path": "skills/typescript-advanced-types/SKILL.md",
|
||||
"sha256": "2c2dea20ce6d164b63aa85cc3010b20fc0beb3d34f52826aaec1c9c41ab27587"
|
||||
},
|
||||
{
|
||||
"path": "skills/modern-javascript-patterns/SKILL.md",
|
||||
"sha256": "98c861b36cc37530499bf48ace5d3f5ca5c0f29fa873b5d24a2d1f40f89af1cd"
|
||||
},
|
||||
{
|
||||
"path": "skills/nodejs-backend-patterns/SKILL.md",
|
||||
"sha256": "ca74d6ff3d515709c471af789ac94a93c0b5c020a52939722f651d40b618c657"
|
||||
}
|
||||
],
|
||||
"dirSha256": "289e93e00b74761c3f14be346f07d0500cf0ff26959c5670036a790700303e36"
|
||||
},
|
||||
"security": {
|
||||
"scannedAt": null,
|
||||
"scannerVersion": null,
|
||||
"flags": []
|
||||
}
|
||||
}
|
||||
1025
skills/javascript-testing-patterns/SKILL.md
Normal file
1025
skills/javascript-testing-patterns/SKILL.md
Normal file
File diff suppressed because it is too large
Load Diff
911
skills/modern-javascript-patterns/SKILL.md
Normal file
911
skills/modern-javascript-patterns/SKILL.md
Normal file
@@ -0,0 +1,911 @@
|
||||
---
|
||||
name: modern-javascript-patterns
|
||||
description: Master ES6+ features including async/await, destructuring, spread operators, arrow functions, promises, modules, iterators, generators, and functional programming patterns for writing clean, efficient JavaScript code. Use when refactoring legacy code, implementing modern patterns, or optimizing JavaScript applications.
|
||||
---
|
||||
|
||||
# Modern JavaScript Patterns
|
||||
|
||||
Comprehensive guide for mastering modern JavaScript (ES6+) features, functional programming patterns, and best practices for writing clean, maintainable, and performant code.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Refactoring legacy JavaScript to modern syntax
|
||||
- Implementing functional programming patterns
|
||||
- Optimizing JavaScript performance
|
||||
- Writing maintainable and readable code
|
||||
- Working with asynchronous operations
|
||||
- Building modern web applications
|
||||
- Migrating from callbacks to Promises/async-await
|
||||
- Implementing data transformation pipelines
|
||||
|
||||
## ES6+ Core Features
|
||||
|
||||
### 1. Arrow Functions
|
||||
|
||||
**Syntax and Use Cases:**
|
||||
```javascript
|
||||
// Traditional function
|
||||
function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// Arrow function
|
||||
const add = (a, b) => a + b;
|
||||
|
||||
// Single parameter (parentheses optional)
|
||||
const double = x => x * 2;
|
||||
|
||||
// No parameters
|
||||
const getRandom = () => Math.random();
|
||||
|
||||
// Multiple statements (need curly braces)
|
||||
const processUser = user => {
|
||||
const normalized = user.name.toLowerCase();
|
||||
return { ...user, name: normalized };
|
||||
};
|
||||
|
||||
// Returning objects (wrap in parentheses)
|
||||
const createUser = (name, age) => ({ name, age });
|
||||
```
|
||||
|
||||
**Lexical 'this' Binding:**
|
||||
```javascript
|
||||
class Counter {
|
||||
constructor() {
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
// Arrow function preserves 'this' context
|
||||
increment = () => {
|
||||
this.count++;
|
||||
};
|
||||
|
||||
// Traditional function loses 'this' in callbacks
|
||||
incrementTraditional() {
|
||||
setTimeout(function() {
|
||||
this.count++; // 'this' is undefined
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// Arrow function maintains 'this'
|
||||
incrementArrow() {
|
||||
setTimeout(() => {
|
||||
this.count++; // 'this' refers to Counter instance
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Destructuring
|
||||
|
||||
**Object Destructuring:**
|
||||
```javascript
|
||||
const user = {
|
||||
id: 1,
|
||||
name: 'John Doe',
|
||||
email: 'john@example.com',
|
||||
address: {
|
||||
city: 'New York',
|
||||
country: 'USA'
|
||||
}
|
||||
};
|
||||
|
||||
// Basic destructuring
|
||||
const { name, email } = user;
|
||||
|
||||
// Rename variables
|
||||
const { name: userName, email: userEmail } = user;
|
||||
|
||||
// Default values
|
||||
const { age = 25 } = user;
|
||||
|
||||
// Nested destructuring
|
||||
const { address: { city, country } } = user;
|
||||
|
||||
// Rest operator
|
||||
const { id, ...userWithoutId } = user;
|
||||
|
||||
// Function parameters
|
||||
function greet({ name, age = 18 }) {
|
||||
console.log(`Hello ${name}, you are ${age}`);
|
||||
}
|
||||
greet(user);
|
||||
```
|
||||
|
||||
**Array Destructuring:**
|
||||
```javascript
|
||||
const numbers = [1, 2, 3, 4, 5];
|
||||
|
||||
// Basic destructuring
|
||||
const [first, second] = numbers;
|
||||
|
||||
// Skip elements
|
||||
const [, , third] = numbers;
|
||||
|
||||
// Rest operator
|
||||
const [head, ...tail] = numbers;
|
||||
|
||||
// Swapping variables
|
||||
let a = 1, b = 2;
|
||||
[a, b] = [b, a];
|
||||
|
||||
// Function return values
|
||||
function getCoordinates() {
|
||||
return [10, 20];
|
||||
}
|
||||
const [x, y] = getCoordinates();
|
||||
|
||||
// Default values
|
||||
const [one, two, three = 0] = [1, 2];
|
||||
```
|
||||
|
||||
### 3. Spread and Rest Operators
|
||||
|
||||
**Spread Operator:**
|
||||
```javascript
|
||||
// Array spreading
|
||||
const arr1 = [1, 2, 3];
|
||||
const arr2 = [4, 5, 6];
|
||||
const combined = [...arr1, ...arr2];
|
||||
|
||||
// Object spreading
|
||||
const defaults = { theme: 'dark', lang: 'en' };
|
||||
const userPrefs = { theme: 'light' };
|
||||
const settings = { ...defaults, ...userPrefs };
|
||||
|
||||
// Function arguments
|
||||
const numbers = [1, 2, 3];
|
||||
Math.max(...numbers);
|
||||
|
||||
// Copying arrays/objects (shallow copy)
|
||||
const copy = [...arr1];
|
||||
const objCopy = { ...user };
|
||||
|
||||
// Adding items immutably
|
||||
const newArr = [...arr1, 4, 5];
|
||||
const newObj = { ...user, age: 30 };
|
||||
```
|
||||
|
||||
**Rest Parameters:**
|
||||
```javascript
|
||||
// Collect function arguments
|
||||
function sum(...numbers) {
|
||||
return numbers.reduce((total, num) => total + num, 0);
|
||||
}
|
||||
sum(1, 2, 3, 4, 5);
|
||||
|
||||
// With regular parameters
|
||||
function greet(greeting, ...names) {
|
||||
return `${greeting} ${names.join(', ')}`;
|
||||
}
|
||||
greet('Hello', 'John', 'Jane', 'Bob');
|
||||
|
||||
// Object rest
|
||||
const { id, ...userData } = user;
|
||||
|
||||
// Array rest
|
||||
const [first, ...rest] = [1, 2, 3, 4, 5];
|
||||
```
|
||||
|
||||
### 4. Template Literals
|
||||
|
||||
```javascript
|
||||
// Basic usage
|
||||
const name = 'John';
|
||||
const greeting = `Hello, ${name}!`;
|
||||
|
||||
// Multi-line strings
|
||||
const html = `
|
||||
<div>
|
||||
<h1>${title}</h1>
|
||||
<p>${content}</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Expression evaluation
|
||||
const price = 19.99;
|
||||
const total = `Total: $${(price * 1.2).toFixed(2)}`;
|
||||
|
||||
// Tagged template literals
|
||||
function highlight(strings, ...values) {
|
||||
return strings.reduce((result, str, i) => {
|
||||
const value = values[i] || '';
|
||||
return result + str + `<mark>${value}</mark>`;
|
||||
}, '');
|
||||
}
|
||||
|
||||
const name = 'John';
|
||||
const age = 30;
|
||||
const html = highlight`Name: ${name}, Age: ${age}`;
|
||||
// Output: "Name: <mark>John</mark>, Age: <mark>30</mark>"
|
||||
```
|
||||
|
||||
### 5. Enhanced Object Literals
|
||||
|
||||
```javascript
|
||||
const name = 'John';
|
||||
const age = 30;
|
||||
|
||||
// Shorthand property names
|
||||
const user = { name, age };
|
||||
|
||||
// Shorthand method names
|
||||
const calculator = {
|
||||
add(a, b) {
|
||||
return a + b;
|
||||
},
|
||||
subtract(a, b) {
|
||||
return a - b;
|
||||
}
|
||||
};
|
||||
|
||||
// Computed property names
|
||||
const field = 'email';
|
||||
const user = {
|
||||
name: 'John',
|
||||
[field]: 'john@example.com',
|
||||
[`get${field.charAt(0).toUpperCase()}${field.slice(1)}`]() {
|
||||
return this[field];
|
||||
}
|
||||
};
|
||||
|
||||
// Dynamic property creation
|
||||
const createUser = (name, ...props) => {
|
||||
return props.reduce((user, [key, value]) => ({
|
||||
...user,
|
||||
[key]: value
|
||||
}), { name });
|
||||
};
|
||||
|
||||
const user = createUser('John', ['age', 30], ['email', 'john@example.com']);
|
||||
```
|
||||
|
||||
## Asynchronous Patterns
|
||||
|
||||
### 1. Promises
|
||||
|
||||
**Creating and Using Promises:**
|
||||
```javascript
|
||||
// Creating a promise
|
||||
const fetchUser = (id) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
if (id > 0) {
|
||||
resolve({ id, name: 'John' });
|
||||
} else {
|
||||
reject(new Error('Invalid ID'));
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
};
|
||||
|
||||
// Using promises
|
||||
fetchUser(1)
|
||||
.then(user => console.log(user))
|
||||
.catch(error => console.error(error))
|
||||
.finally(() => console.log('Done'));
|
||||
|
||||
// Chaining promises
|
||||
fetchUser(1)
|
||||
.then(user => fetchUserPosts(user.id))
|
||||
.then(posts => processPosts(posts))
|
||||
.then(result => console.log(result))
|
||||
.catch(error => console.error(error));
|
||||
```
|
||||
|
||||
**Promise Combinators:**
|
||||
```javascript
|
||||
// Promise.all - Wait for all promises
|
||||
const promises = [
|
||||
fetchUser(1),
|
||||
fetchUser(2),
|
||||
fetchUser(3)
|
||||
];
|
||||
|
||||
Promise.all(promises)
|
||||
.then(users => console.log(users))
|
||||
.catch(error => console.error('At least one failed:', error));
|
||||
|
||||
// Promise.allSettled - Wait for all, regardless of outcome
|
||||
Promise.allSettled(promises)
|
||||
.then(results => {
|
||||
results.forEach(result => {
|
||||
if (result.status === 'fulfilled') {
|
||||
console.log('Success:', result.value);
|
||||
} else {
|
||||
console.log('Error:', result.reason);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Promise.race - First to complete
|
||||
Promise.race(promises)
|
||||
.then(winner => console.log('First:', winner))
|
||||
.catch(error => console.error(error));
|
||||
|
||||
// Promise.any - First to succeed
|
||||
Promise.any(promises)
|
||||
.then(first => console.log('First success:', first))
|
||||
.catch(error => console.error('All failed:', error));
|
||||
```
|
||||
|
||||
### 2. Async/Await
|
||||
|
||||
**Basic Usage:**
|
||||
```javascript
|
||||
// Async function always returns a Promise
|
||||
async function fetchUser(id) {
|
||||
const response = await fetch(`/api/users/${id}`);
|
||||
const user = await response.json();
|
||||
return user;
|
||||
}
|
||||
|
||||
// Error handling with try/catch
|
||||
async function getUserData(id) {
|
||||
try {
|
||||
const user = await fetchUser(id);
|
||||
const posts = await fetchUserPosts(user.id);
|
||||
return { user, posts };
|
||||
} catch (error) {
|
||||
console.error('Error fetching data:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Sequential vs Parallel execution
|
||||
async function sequential() {
|
||||
const user1 = await fetchUser(1); // Wait
|
||||
const user2 = await fetchUser(2); // Then wait
|
||||
return [user1, user2];
|
||||
}
|
||||
|
||||
async function parallel() {
|
||||
const [user1, user2] = await Promise.all([
|
||||
fetchUser(1),
|
||||
fetchUser(2)
|
||||
]);
|
||||
return [user1, user2];
|
||||
}
|
||||
```
|
||||
|
||||
**Advanced Patterns:**
|
||||
```javascript
|
||||
// Async IIFE
|
||||
(async () => {
|
||||
const result = await someAsyncOperation();
|
||||
console.log(result);
|
||||
})();
|
||||
|
||||
// Async iteration
|
||||
async function processUsers(userIds) {
|
||||
for (const id of userIds) {
|
||||
const user = await fetchUser(id);
|
||||
await processUser(user);
|
||||
}
|
||||
}
|
||||
|
||||
// Top-level await (ES2022)
|
||||
const config = await fetch('/config.json').then(r => r.json());
|
||||
|
||||
// Retry logic
|
||||
async function fetchWithRetry(url, retries = 3) {
|
||||
for (let i = 0; i < retries; i++) {
|
||||
try {
|
||||
return await fetch(url);
|
||||
} catch (error) {
|
||||
if (i === retries - 1) throw error;
|
||||
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Timeout wrapper
|
||||
async function withTimeout(promise, ms) {
|
||||
const timeout = new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('Timeout')), ms)
|
||||
);
|
||||
return Promise.race([promise, timeout]);
|
||||
}
|
||||
```
|
||||
|
||||
## Functional Programming Patterns
|
||||
|
||||
### 1. Array Methods
|
||||
|
||||
**Map, Filter, Reduce:**
|
||||
```javascript
|
||||
const users = [
|
||||
{ id: 1, name: 'John', age: 30, active: true },
|
||||
{ id: 2, name: 'Jane', age: 25, active: false },
|
||||
{ id: 3, name: 'Bob', age: 35, active: true }
|
||||
];
|
||||
|
||||
// Map - Transform array
|
||||
const names = users.map(user => user.name);
|
||||
const upperNames = users.map(user => user.name.toUpperCase());
|
||||
|
||||
// Filter - Select elements
|
||||
const activeUsers = users.filter(user => user.active);
|
||||
const adults = users.filter(user => user.age >= 18);
|
||||
|
||||
// Reduce - Aggregate data
|
||||
const totalAge = users.reduce((sum, user) => sum + user.age, 0);
|
||||
const avgAge = totalAge / users.length;
|
||||
|
||||
// Group by property
|
||||
const byActive = users.reduce((groups, user) => {
|
||||
const key = user.active ? 'active' : 'inactive';
|
||||
return {
|
||||
...groups,
|
||||
[key]: [...(groups[key] || []), user]
|
||||
};
|
||||
}, {});
|
||||
|
||||
// Chaining methods
|
||||
const result = users
|
||||
.filter(user => user.active)
|
||||
.map(user => user.name)
|
||||
.sort()
|
||||
.join(', ');
|
||||
```
|
||||
|
||||
**Advanced Array Methods:**
|
||||
```javascript
|
||||
// Find - First matching element
|
||||
const user = users.find(u => u.id === 2);
|
||||
|
||||
// FindIndex - Index of first match
|
||||
const index = users.findIndex(u => u.name === 'Jane');
|
||||
|
||||
// Some - At least one matches
|
||||
const hasActive = users.some(u => u.active);
|
||||
|
||||
// Every - All match
|
||||
const allAdults = users.every(u => u.age >= 18);
|
||||
|
||||
// FlatMap - Map and flatten
|
||||
const userTags = [
|
||||
{ name: 'John', tags: ['admin', 'user'] },
|
||||
{ name: 'Jane', tags: ['user'] }
|
||||
];
|
||||
const allTags = userTags.flatMap(u => u.tags);
|
||||
|
||||
// From - Create array from iterable
|
||||
const str = 'hello';
|
||||
const chars = Array.from(str);
|
||||
const numbers = Array.from({ length: 5 }, (_, i) => i + 1);
|
||||
|
||||
// Of - Create array from arguments
|
||||
const arr = Array.of(1, 2, 3);
|
||||
```
|
||||
|
||||
### 2. Higher-Order Functions
|
||||
|
||||
**Functions as Arguments:**
|
||||
```javascript
|
||||
// Custom forEach
|
||||
function forEach(array, callback) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
callback(array[i], i, array);
|
||||
}
|
||||
}
|
||||
|
||||
// Custom map
|
||||
function map(array, transform) {
|
||||
const result = [];
|
||||
for (const item of array) {
|
||||
result.push(transform(item));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Custom filter
|
||||
function filter(array, predicate) {
|
||||
const result = [];
|
||||
for (const item of array) {
|
||||
if (predicate(item)) {
|
||||
result.push(item);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
**Functions Returning Functions:**
|
||||
```javascript
|
||||
// Currying
|
||||
const multiply = a => b => a * b;
|
||||
const double = multiply(2);
|
||||
const triple = multiply(3);
|
||||
|
||||
console.log(double(5)); // 10
|
||||
console.log(triple(5)); // 15
|
||||
|
||||
// Partial application
|
||||
function partial(fn, ...args) {
|
||||
return (...moreArgs) => fn(...args, ...moreArgs);
|
||||
}
|
||||
|
||||
const add = (a, b, c) => a + b + c;
|
||||
const add5 = partial(add, 5);
|
||||
console.log(add5(3, 2)); // 10
|
||||
|
||||
// Memoization
|
||||
function memoize(fn) {
|
||||
const cache = new Map();
|
||||
return (...args) => {
|
||||
const key = JSON.stringify(args);
|
||||
if (cache.has(key)) {
|
||||
return cache.get(key);
|
||||
}
|
||||
const result = fn(...args);
|
||||
cache.set(key, result);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
const fibonacci = memoize((n) => {
|
||||
if (n <= 1) return n;
|
||||
return fibonacci(n - 1) + fibonacci(n - 2);
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Composition and Piping
|
||||
|
||||
```javascript
|
||||
// Function composition
|
||||
const compose = (...fns) => x =>
|
||||
fns.reduceRight((acc, fn) => fn(acc), x);
|
||||
|
||||
const pipe = (...fns) => x =>
|
||||
fns.reduce((acc, fn) => fn(acc), x);
|
||||
|
||||
// Example usage
|
||||
const addOne = x => x + 1;
|
||||
const double = x => x * 2;
|
||||
const square = x => x * x;
|
||||
|
||||
const composed = compose(square, double, addOne);
|
||||
console.log(composed(3)); // ((3 + 1) * 2)^2 = 64
|
||||
|
||||
const piped = pipe(addOne, double, square);
|
||||
console.log(piped(3)); // ((3 + 1) * 2)^2 = 64
|
||||
|
||||
// Practical example
|
||||
const processUser = pipe(
|
||||
user => ({ ...user, name: user.name.trim() }),
|
||||
user => ({ ...user, email: user.email.toLowerCase() }),
|
||||
user => ({ ...user, age: parseInt(user.age) })
|
||||
);
|
||||
|
||||
const user = processUser({
|
||||
name: ' John ',
|
||||
email: 'JOHN@EXAMPLE.COM',
|
||||
age: '30'
|
||||
});
|
||||
```
|
||||
|
||||
### 4. Pure Functions and Immutability
|
||||
|
||||
```javascript
|
||||
// Impure function (modifies input)
|
||||
function addItemImpure(cart, item) {
|
||||
cart.items.push(item);
|
||||
cart.total += item.price;
|
||||
return cart;
|
||||
}
|
||||
|
||||
// Pure function (no side effects)
|
||||
function addItemPure(cart, item) {
|
||||
return {
|
||||
...cart,
|
||||
items: [...cart.items, item],
|
||||
total: cart.total + item.price
|
||||
};
|
||||
}
|
||||
|
||||
// Immutable array operations
|
||||
const numbers = [1, 2, 3, 4, 5];
|
||||
|
||||
// Add to array
|
||||
const withSix = [...numbers, 6];
|
||||
|
||||
// Remove from array
|
||||
const withoutThree = numbers.filter(n => n !== 3);
|
||||
|
||||
// Update array element
|
||||
const doubled = numbers.map(n => n === 3 ? n * 2 : n);
|
||||
|
||||
// Immutable object operations
|
||||
const user = { name: 'John', age: 30 };
|
||||
|
||||
// Update property
|
||||
const olderUser = { ...user, age: 31 };
|
||||
|
||||
// Add property
|
||||
const withEmail = { ...user, email: 'john@example.com' };
|
||||
|
||||
// Remove property
|
||||
const { age, ...withoutAge } = user;
|
||||
|
||||
// Deep cloning (simple approach)
|
||||
const deepClone = obj => JSON.parse(JSON.stringify(obj));
|
||||
|
||||
// Better deep cloning
|
||||
const structuredClone = obj => globalThis.structuredClone(obj);
|
||||
```
|
||||
|
||||
## Modern Class Features
|
||||
|
||||
```javascript
|
||||
// Class syntax
|
||||
class User {
|
||||
// Private fields
|
||||
#password;
|
||||
|
||||
// Public fields
|
||||
id;
|
||||
name;
|
||||
|
||||
// Static field
|
||||
static count = 0;
|
||||
|
||||
constructor(id, name, password) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.#password = password;
|
||||
User.count++;
|
||||
}
|
||||
|
||||
// Public method
|
||||
greet() {
|
||||
return `Hello, ${this.name}`;
|
||||
}
|
||||
|
||||
// Private method
|
||||
#hashPassword(password) {
|
||||
return `hashed_${password}`;
|
||||
}
|
||||
|
||||
// Getter
|
||||
get displayName() {
|
||||
return this.name.toUpperCase();
|
||||
}
|
||||
|
||||
// Setter
|
||||
set password(newPassword) {
|
||||
this.#password = this.#hashPassword(newPassword);
|
||||
}
|
||||
|
||||
// Static method
|
||||
static create(id, name, password) {
|
||||
return new User(id, name, password);
|
||||
}
|
||||
}
|
||||
|
||||
// Inheritance
|
||||
class Admin extends User {
|
||||
constructor(id, name, password, role) {
|
||||
super(id, name, password);
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
greet() {
|
||||
return `${super.greet()}, I'm an admin`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Modules (ES6)
|
||||
|
||||
```javascript
|
||||
// Exporting
|
||||
// math.js
|
||||
export const PI = 3.14159;
|
||||
export function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
export class Calculator {
|
||||
// ...
|
||||
}
|
||||
|
||||
// Default export
|
||||
export default function multiply(a, b) {
|
||||
return a * b;
|
||||
}
|
||||
|
||||
// Importing
|
||||
// app.js
|
||||
import multiply, { PI, add, Calculator } from './math.js';
|
||||
|
||||
// Rename imports
|
||||
import { add as sum } from './math.js';
|
||||
|
||||
// Import all
|
||||
import * as Math from './math.js';
|
||||
|
||||
// Dynamic imports
|
||||
const module = await import('./math.js');
|
||||
const { add } = await import('./math.js');
|
||||
|
||||
// Conditional loading
|
||||
if (condition) {
|
||||
const module = await import('./feature.js');
|
||||
module.init();
|
||||
}
|
||||
```
|
||||
|
||||
## Iterators and Generators
|
||||
|
||||
```javascript
|
||||
// Custom iterator
|
||||
const range = {
|
||||
from: 1,
|
||||
to: 5,
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return {
|
||||
current: this.from,
|
||||
last: this.to,
|
||||
|
||||
next() {
|
||||
if (this.current <= this.last) {
|
||||
return { done: false, value: this.current++ };
|
||||
} else {
|
||||
return { done: true };
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
for (const num of range) {
|
||||
console.log(num); // 1, 2, 3, 4, 5
|
||||
}
|
||||
|
||||
// Generator function
|
||||
function* rangeGenerator(from, to) {
|
||||
for (let i = from; i <= to; i++) {
|
||||
yield i;
|
||||
}
|
||||
}
|
||||
|
||||
for (const num of rangeGenerator(1, 5)) {
|
||||
console.log(num);
|
||||
}
|
||||
|
||||
// Infinite generator
|
||||
function* fibonacci() {
|
||||
let [prev, curr] = [0, 1];
|
||||
while (true) {
|
||||
yield curr;
|
||||
[prev, curr] = [curr, prev + curr];
|
||||
}
|
||||
}
|
||||
|
||||
// Async generator
|
||||
async function* fetchPages(url) {
|
||||
let page = 1;
|
||||
while (true) {
|
||||
const response = await fetch(`${url}?page=${page}`);
|
||||
const data = await response.json();
|
||||
if (data.length === 0) break;
|
||||
yield data;
|
||||
page++;
|
||||
}
|
||||
}
|
||||
|
||||
for await (const page of fetchPages('/api/users')) {
|
||||
console.log(page);
|
||||
}
|
||||
```
|
||||
|
||||
## Modern Operators
|
||||
|
||||
```javascript
|
||||
// Optional chaining
|
||||
const user = { name: 'John', address: { city: 'NYC' } };
|
||||
const city = user?.address?.city;
|
||||
const zipCode = user?.address?.zipCode; // undefined
|
||||
|
||||
// Function call
|
||||
const result = obj.method?.();
|
||||
|
||||
// Array access
|
||||
const first = arr?.[0];
|
||||
|
||||
// Nullish coalescing
|
||||
const value = null ?? 'default'; // 'default'
|
||||
const value = undefined ?? 'default'; // 'default'
|
||||
const value = 0 ?? 'default'; // 0 (not 'default')
|
||||
const value = '' ?? 'default'; // '' (not 'default')
|
||||
|
||||
// Logical assignment
|
||||
let a = null;
|
||||
a ??= 'default'; // a = 'default'
|
||||
|
||||
let b = 5;
|
||||
b ??= 10; // b = 5 (unchanged)
|
||||
|
||||
let obj = { count: 0 };
|
||||
obj.count ||= 1; // obj.count = 1
|
||||
obj.count &&= 2; // obj.count = 2
|
||||
```
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
```javascript
|
||||
// Debounce
|
||||
function debounce(fn, delay) {
|
||||
let timeoutId;
|
||||
return (...args) => {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(() => fn(...args), delay);
|
||||
};
|
||||
}
|
||||
|
||||
const searchDebounced = debounce(search, 300);
|
||||
|
||||
// Throttle
|
||||
function throttle(fn, limit) {
|
||||
let inThrottle;
|
||||
return (...args) => {
|
||||
if (!inThrottle) {
|
||||
fn(...args);
|
||||
inThrottle = true;
|
||||
setTimeout(() => inThrottle = false, limit);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const scrollThrottled = throttle(handleScroll, 100);
|
||||
|
||||
// Lazy evaluation
|
||||
function* lazyMap(iterable, transform) {
|
||||
for (const item of iterable) {
|
||||
yield transform(item);
|
||||
}
|
||||
}
|
||||
|
||||
// Use only what you need
|
||||
const numbers = [1, 2, 3, 4, 5];
|
||||
const doubled = lazyMap(numbers, x => x * 2);
|
||||
const first = doubled.next().value; // Only computes first value
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use const by default**: Only use let when reassignment is needed
|
||||
2. **Prefer arrow functions**: Especially for callbacks
|
||||
3. **Use template literals**: Instead of string concatenation
|
||||
4. **Destructure objects and arrays**: For cleaner code
|
||||
5. **Use async/await**: Instead of Promise chains
|
||||
6. **Avoid mutating data**: Use spread operator and array methods
|
||||
7. **Use optional chaining**: Prevent "Cannot read property of undefined"
|
||||
8. **Use nullish coalescing**: For default values
|
||||
9. **Prefer array methods**: Over traditional loops
|
||||
10. **Use modules**: For better code organization
|
||||
11. **Write pure functions**: Easier to test and reason about
|
||||
12. **Use meaningful variable names**: Self-documenting code
|
||||
13. **Keep functions small**: Single responsibility principle
|
||||
14. **Handle errors properly**: Use try/catch with async/await
|
||||
15. **Use strict mode**: `'use strict'` for better error catching
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
1. **this binding confusion**: Use arrow functions or bind()
|
||||
2. **Async/await without error handling**: Always use try/catch
|
||||
3. **Promise creation unnecessary**: Don't wrap already async functions
|
||||
4. **Mutation of objects**: Use spread operator or Object.assign()
|
||||
5. **Forgetting await**: Async functions return promises
|
||||
6. **Blocking event loop**: Avoid synchronous operations
|
||||
7. **Memory leaks**: Clean up event listeners and timers
|
||||
8. **Not handling promise rejections**: Use catch() or try/catch
|
||||
|
||||
## Resources
|
||||
|
||||
- **MDN Web Docs**: https://developer.mozilla.org/en-US/docs/Web/JavaScript
|
||||
- **JavaScript.info**: https://javascript.info/
|
||||
- **You Don't Know JS**: https://github.com/getify/You-Dont-Know-JS
|
||||
- **Eloquent JavaScript**: https://eloquentjavascript.net/
|
||||
- **ES6 Features**: http://es6-features.org/
|
||||
1020
skills/nodejs-backend-patterns/SKILL.md
Normal file
1020
skills/nodejs-backend-patterns/SKILL.md
Normal file
File diff suppressed because it is too large
Load Diff
717
skills/typescript-advanced-types/SKILL.md
Normal file
717
skills/typescript-advanced-types/SKILL.md
Normal file
@@ -0,0 +1,717 @@
|
||||
---
|
||||
name: typescript-advanced-types
|
||||
description: Master TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript projects.
|
||||
---
|
||||
|
||||
# TypeScript Advanced Types
|
||||
|
||||
Comprehensive guidance for mastering TypeScript's advanced type system including generics, conditional types, mapped types, template literal types, and utility types for building robust, type-safe applications.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Building type-safe libraries or frameworks
|
||||
- Creating reusable generic components
|
||||
- Implementing complex type inference logic
|
||||
- Designing type-safe API clients
|
||||
- Building form validation systems
|
||||
- Creating strongly-typed configuration objects
|
||||
- Implementing type-safe state management
|
||||
- Migrating JavaScript codebases to TypeScript
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### 1. Generics
|
||||
|
||||
**Purpose:** Create reusable, type-flexible components while maintaining type safety.
|
||||
|
||||
**Basic Generic Function:**
|
||||
```typescript
|
||||
function identity<T>(value: T): T {
|
||||
return value;
|
||||
}
|
||||
|
||||
const num = identity<number>(42); // Type: number
|
||||
const str = identity<string>("hello"); // Type: string
|
||||
const auto = identity(true); // Type inferred: boolean
|
||||
```
|
||||
|
||||
**Generic Constraints:**
|
||||
```typescript
|
||||
interface HasLength {
|
||||
length: number;
|
||||
}
|
||||
|
||||
function logLength<T extends HasLength>(item: T): T {
|
||||
console.log(item.length);
|
||||
return item;
|
||||
}
|
||||
|
||||
logLength("hello"); // OK: string has length
|
||||
logLength([1, 2, 3]); // OK: array has length
|
||||
logLength({ length: 10 }); // OK: object has length
|
||||
// logLength(42); // Error: number has no length
|
||||
```
|
||||
|
||||
**Multiple Type Parameters:**
|
||||
```typescript
|
||||
function merge<T, U>(obj1: T, obj2: U): T & U {
|
||||
return { ...obj1, ...obj2 };
|
||||
}
|
||||
|
||||
const merged = merge(
|
||||
{ name: "John" },
|
||||
{ age: 30 }
|
||||
);
|
||||
// Type: { name: string } & { age: number }
|
||||
```
|
||||
|
||||
### 2. Conditional Types
|
||||
|
||||
**Purpose:** Create types that depend on conditions, enabling sophisticated type logic.
|
||||
|
||||
**Basic Conditional Type:**
|
||||
```typescript
|
||||
type IsString<T> = T extends string ? true : false;
|
||||
|
||||
type A = IsString<string>; // true
|
||||
type B = IsString<number>; // false
|
||||
```
|
||||
|
||||
**Extracting Return Types:**
|
||||
```typescript
|
||||
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
|
||||
|
||||
function getUser() {
|
||||
return { id: 1, name: "John" };
|
||||
}
|
||||
|
||||
type User = ReturnType<typeof getUser>;
|
||||
// Type: { id: number; name: string; }
|
||||
```
|
||||
|
||||
**Distributive Conditional Types:**
|
||||
```typescript
|
||||
type ToArray<T> = T extends any ? T[] : never;
|
||||
|
||||
type StrOrNumArray = ToArray<string | number>;
|
||||
// Type: string[] | number[]
|
||||
```
|
||||
|
||||
**Nested Conditions:**
|
||||
```typescript
|
||||
type TypeName<T> =
|
||||
T extends string ? "string" :
|
||||
T extends number ? "number" :
|
||||
T extends boolean ? "boolean" :
|
||||
T extends undefined ? "undefined" :
|
||||
T extends Function ? "function" :
|
||||
"object";
|
||||
|
||||
type T1 = TypeName<string>; // "string"
|
||||
type T2 = TypeName<() => void>; // "function"
|
||||
```
|
||||
|
||||
### 3. Mapped Types
|
||||
|
||||
**Purpose:** Transform existing types by iterating over their properties.
|
||||
|
||||
**Basic Mapped Type:**
|
||||
```typescript
|
||||
type Readonly<T> = {
|
||||
readonly [P in keyof T]: T[P];
|
||||
};
|
||||
|
||||
interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
type ReadonlyUser = Readonly<User>;
|
||||
// Type: { readonly id: number; readonly name: string; }
|
||||
```
|
||||
|
||||
**Optional Properties:**
|
||||
```typescript
|
||||
type Partial<T> = {
|
||||
[P in keyof T]?: T[P];
|
||||
};
|
||||
|
||||
type PartialUser = Partial<User>;
|
||||
// Type: { id?: number; name?: string; }
|
||||
```
|
||||
|
||||
**Key Remapping:**
|
||||
```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: { getName: () => string; getAge: () => number; }
|
||||
```
|
||||
|
||||
**Filtering Properties:**
|
||||
```typescript
|
||||
type PickByType<T, U> = {
|
||||
[K in keyof T as T[K] extends U ? K : never]: T[K]
|
||||
};
|
||||
|
||||
interface Mixed {
|
||||
id: number;
|
||||
name: string;
|
||||
age: number;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
type OnlyNumbers = PickByType<Mixed, number>;
|
||||
// Type: { id: number; age: number; }
|
||||
```
|
||||
|
||||
### 4. Template Literal Types
|
||||
|
||||
**Purpose:** Create string-based types with pattern matching and transformation.
|
||||
|
||||
**Basic Template Literal:**
|
||||
```typescript
|
||||
type EventName = "click" | "focus" | "blur";
|
||||
type EventHandler = `on${Capitalize<EventName>}`;
|
||||
// Type: "onClick" | "onFocus" | "onBlur"
|
||||
```
|
||||
|
||||
**String Manipulation:**
|
||||
```typescript
|
||||
type UppercaseGreeting = Uppercase<"hello">; // "HELLO"
|
||||
type LowercaseGreeting = Lowercase<"HELLO">; // "hello"
|
||||
type CapitalizedName = Capitalize<"john">; // "John"
|
||||
type UncapitalizedName = Uncapitalize<"John">; // "john"
|
||||
```
|
||||
|
||||
**Path Building:**
|
||||
```typescript
|
||||
type Path<T> = T extends object
|
||||
? { [K in keyof T]: K extends string
|
||||
? `${K}` | `${K}.${Path<T[K]>}`
|
||||
: never
|
||||
}[keyof T]
|
||||
: never;
|
||||
|
||||
interface Config {
|
||||
server: {
|
||||
host: string;
|
||||
port: number;
|
||||
};
|
||||
database: {
|
||||
url: string;
|
||||
};
|
||||
}
|
||||
|
||||
type ConfigPath = Path<Config>;
|
||||
// Type: "server" | "database" | "server.host" | "server.port" | "database.url"
|
||||
```
|
||||
|
||||
### 5. Utility Types
|
||||
|
||||
**Built-in Utility Types:**
|
||||
|
||||
```typescript
|
||||
// Partial<T> - Make all properties optional
|
||||
type PartialUser = Partial<User>;
|
||||
|
||||
// Required<T> - Make all properties required
|
||||
type RequiredUser = Required<PartialUser>;
|
||||
|
||||
// Readonly<T> - Make all properties readonly
|
||||
type ReadonlyUser = Readonly<User>;
|
||||
|
||||
// Pick<T, K> - Select specific properties
|
||||
type UserName = Pick<User, "name" | "email">;
|
||||
|
||||
// Omit<T, K> - Remove specific properties
|
||||
type UserWithoutPassword = Omit<User, "password">;
|
||||
|
||||
// Exclude<T, U> - Exclude types from union
|
||||
type T1 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
|
||||
|
||||
// Extract<T, U> - Extract types from union
|
||||
type T2 = Extract<"a" | "b" | "c", "a" | "b">; // "a" | "b"
|
||||
|
||||
// NonNullable<T> - Exclude null and undefined
|
||||
type T3 = NonNullable<string | null | undefined>; // string
|
||||
|
||||
// Record<K, T> - Create object type with keys K and values T
|
||||
type PageInfo = Record<"home" | "about", { title: string }>;
|
||||
```
|
||||
|
||||
## Advanced Patterns
|
||||
|
||||
### Pattern 1: Type-Safe Event Emitter
|
||||
|
||||
```typescript
|
||||
type EventMap = {
|
||||
"user:created": { id: string; name: string };
|
||||
"user:updated": { id: string };
|
||||
"user:deleted": { id: string };
|
||||
};
|
||||
|
||||
class TypedEventEmitter<T extends Record<string, any>> {
|
||||
private listeners: {
|
||||
[K in keyof T]?: Array<(data: T[K]) => void>;
|
||||
} = {};
|
||||
|
||||
on<K extends keyof T>(event: K, callback: (data: T[K]) => void): void {
|
||||
if (!this.listeners[event]) {
|
||||
this.listeners[event] = [];
|
||||
}
|
||||
this.listeners[event]!.push(callback);
|
||||
}
|
||||
|
||||
emit<K extends keyof T>(event: K, data: T[K]): void {
|
||||
const callbacks = this.listeners[event];
|
||||
if (callbacks) {
|
||||
callbacks.forEach(callback => callback(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const emitter = new TypedEventEmitter<EventMap>();
|
||||
|
||||
emitter.on("user:created", (data) => {
|
||||
console.log(data.id, data.name); // Type-safe!
|
||||
});
|
||||
|
||||
emitter.emit("user:created", { id: "1", name: "John" });
|
||||
// emitter.emit("user:created", { id: "1" }); // Error: missing 'name'
|
||||
```
|
||||
|
||||
### Pattern 2: Type-Safe API Client
|
||||
|
||||
```typescript
|
||||
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
|
||||
|
||||
type EndpointConfig = {
|
||||
"/users": {
|
||||
GET: { response: User[] };
|
||||
POST: { body: { name: string; email: string }; response: User };
|
||||
};
|
||||
"/users/:id": {
|
||||
GET: { params: { id: string }; response: User };
|
||||
PUT: { params: { id: string }; body: Partial<User>; response: User };
|
||||
DELETE: { params: { id: string }; response: void };
|
||||
};
|
||||
};
|
||||
|
||||
type ExtractParams<T> = T extends { params: infer P } ? P : never;
|
||||
type ExtractBody<T> = T extends { body: infer B } ? B : never;
|
||||
type ExtractResponse<T> = T extends { response: infer R } ? R : never;
|
||||
|
||||
class APIClient<Config extends Record<string, Record<HTTPMethod, any>>> {
|
||||
async request<
|
||||
Path extends keyof Config,
|
||||
Method extends keyof Config[Path]
|
||||
>(
|
||||
path: Path,
|
||||
method: Method,
|
||||
...[options]: ExtractParams<Config[Path][Method]> extends never
|
||||
? ExtractBody<Config[Path][Method]> extends never
|
||||
? []
|
||||
: [{ body: ExtractBody<Config[Path][Method]> }]
|
||||
: [{
|
||||
params: ExtractParams<Config[Path][Method]>;
|
||||
body?: ExtractBody<Config[Path][Method]>;
|
||||
}]
|
||||
): Promise<ExtractResponse<Config[Path][Method]>> {
|
||||
// Implementation here
|
||||
return {} as any;
|
||||
}
|
||||
}
|
||||
|
||||
const api = new APIClient<EndpointConfig>();
|
||||
|
||||
// Type-safe API calls
|
||||
const users = await api.request("/users", "GET");
|
||||
// Type: User[]
|
||||
|
||||
const newUser = await api.request("/users", "POST", {
|
||||
body: { name: "John", email: "john@example.com" }
|
||||
});
|
||||
// Type: User
|
||||
|
||||
const user = await api.request("/users/:id", "GET", {
|
||||
params: { id: "123" }
|
||||
});
|
||||
// Type: User
|
||||
```
|
||||
|
||||
### Pattern 3: Builder Pattern with Type Safety
|
||||
|
||||
```typescript
|
||||
type BuilderState<T> = {
|
||||
[K in keyof T]: T[K] | undefined;
|
||||
};
|
||||
|
||||
type RequiredKeys<T> = {
|
||||
[K in keyof T]-?: {} extends Pick<T, K> ? never : K;
|
||||
}[keyof T];
|
||||
|
||||
type OptionalKeys<T> = {
|
||||
[K in keyof T]-?: {} extends Pick<T, K> ? K : never;
|
||||
}[keyof T];
|
||||
|
||||
type IsComplete<T, S> =
|
||||
RequiredKeys<T> extends keyof S
|
||||
? S[RequiredKeys<T>] extends undefined
|
||||
? false
|
||||
: true
|
||||
: false;
|
||||
|
||||
class Builder<T, S extends BuilderState<T> = {}> {
|
||||
private state: S = {} as S;
|
||||
|
||||
set<K extends keyof T>(
|
||||
key: K,
|
||||
value: T[K]
|
||||
): Builder<T, S & Record<K, T[K]>> {
|
||||
this.state[key] = value;
|
||||
return this as any;
|
||||
}
|
||||
|
||||
build(
|
||||
this: IsComplete<T, S> extends true ? this : never
|
||||
): T {
|
||||
return this.state as T;
|
||||
}
|
||||
}
|
||||
|
||||
interface User {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
age?: number;
|
||||
}
|
||||
|
||||
const builder = new Builder<User>();
|
||||
|
||||
const user = builder
|
||||
.set("id", "1")
|
||||
.set("name", "John")
|
||||
.set("email", "john@example.com")
|
||||
.build(); // OK: all required fields set
|
||||
|
||||
// const incomplete = builder
|
||||
// .set("id", "1")
|
||||
// .build(); // Error: missing required fields
|
||||
```
|
||||
|
||||
### Pattern 4: Deep Readonly/Partial
|
||||
|
||||
```typescript
|
||||
type DeepReadonly<T> = {
|
||||
readonly [P in keyof T]: T[P] extends object
|
||||
? T[P] extends Function
|
||||
? T[P]
|
||||
: DeepReadonly<T[P]>
|
||||
: T[P];
|
||||
};
|
||||
|
||||
type DeepPartial<T> = {
|
||||
[P in keyof T]?: T[P] extends object
|
||||
? T[P] extends Array<infer U>
|
||||
? Array<DeepPartial<U>>
|
||||
: DeepPartial<T[P]>
|
||||
: T[P];
|
||||
};
|
||||
|
||||
interface Config {
|
||||
server: {
|
||||
host: string;
|
||||
port: number;
|
||||
ssl: {
|
||||
enabled: boolean;
|
||||
cert: string;
|
||||
};
|
||||
};
|
||||
database: {
|
||||
url: string;
|
||||
pool: {
|
||||
min: number;
|
||||
max: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
type ReadonlyConfig = DeepReadonly<Config>;
|
||||
// All nested properties are readonly
|
||||
|
||||
type PartialConfig = DeepPartial<Config>;
|
||||
// All nested properties are optional
|
||||
```
|
||||
|
||||
### Pattern 5: Type-Safe Form Validation
|
||||
|
||||
```typescript
|
||||
type ValidationRule<T> = {
|
||||
validate: (value: T) => boolean;
|
||||
message: string;
|
||||
};
|
||||
|
||||
type FieldValidation<T> = {
|
||||
[K in keyof T]?: ValidationRule<T[K]>[];
|
||||
};
|
||||
|
||||
type ValidationErrors<T> = {
|
||||
[K in keyof T]?: string[];
|
||||
};
|
||||
|
||||
class FormValidator<T extends Record<string, any>> {
|
||||
constructor(private rules: FieldValidation<T>) {}
|
||||
|
||||
validate(data: T): ValidationErrors<T> | null {
|
||||
const errors: ValidationErrors<T> = {};
|
||||
let hasErrors = false;
|
||||
|
||||
for (const key in this.rules) {
|
||||
const fieldRules = this.rules[key];
|
||||
const value = data[key];
|
||||
|
||||
if (fieldRules) {
|
||||
const fieldErrors: string[] = [];
|
||||
|
||||
for (const rule of fieldRules) {
|
||||
if (!rule.validate(value)) {
|
||||
fieldErrors.push(rule.message);
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldErrors.length > 0) {
|
||||
errors[key] = fieldErrors;
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hasErrors ? errors : null;
|
||||
}
|
||||
}
|
||||
|
||||
interface LoginForm {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
const validator = new FormValidator<LoginForm>({
|
||||
email: [
|
||||
{
|
||||
validate: (v) => v.includes("@"),
|
||||
message: "Email must contain @"
|
||||
},
|
||||
{
|
||||
validate: (v) => v.length > 0,
|
||||
message: "Email is required"
|
||||
}
|
||||
],
|
||||
password: [
|
||||
{
|
||||
validate: (v) => v.length >= 8,
|
||||
message: "Password must be at least 8 characters"
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const errors = validator.validate({
|
||||
email: "invalid",
|
||||
password: "short"
|
||||
});
|
||||
// Type: { email?: string[]; password?: string[]; } | null
|
||||
```
|
||||
|
||||
### Pattern 6: Discriminated Unions
|
||||
|
||||
```typescript
|
||||
type Success<T> = {
|
||||
status: "success";
|
||||
data: T;
|
||||
};
|
||||
|
||||
type Error = {
|
||||
status: "error";
|
||||
error: string;
|
||||
};
|
||||
|
||||
type Loading = {
|
||||
status: "loading";
|
||||
};
|
||||
|
||||
type AsyncState<T> = Success<T> | Error | Loading;
|
||||
|
||||
function handleState<T>(state: AsyncState<T>): void {
|
||||
switch (state.status) {
|
||||
case "success":
|
||||
console.log(state.data); // Type: T
|
||||
break;
|
||||
case "error":
|
||||
console.log(state.error); // Type: string
|
||||
break;
|
||||
case "loading":
|
||||
console.log("Loading...");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Type-safe state machine
|
||||
type State =
|
||||
| { type: "idle" }
|
||||
| { type: "fetching"; requestId: string }
|
||||
| { type: "success"; data: any }
|
||||
| { type: "error"; error: Error };
|
||||
|
||||
type Event =
|
||||
| { type: "FETCH"; requestId: string }
|
||||
| { type: "SUCCESS"; data: any }
|
||||
| { type: "ERROR"; error: Error }
|
||||
| { type: "RESET" };
|
||||
|
||||
function reducer(state: State, event: Event): State {
|
||||
switch (state.type) {
|
||||
case "idle":
|
||||
return event.type === "FETCH"
|
||||
? { type: "fetching", requestId: event.requestId }
|
||||
: state;
|
||||
case "fetching":
|
||||
if (event.type === "SUCCESS") {
|
||||
return { type: "success", data: event.data };
|
||||
}
|
||||
if (event.type === "ERROR") {
|
||||
return { type: "error", error: event.error };
|
||||
}
|
||||
return state;
|
||||
case "success":
|
||||
case "error":
|
||||
return event.type === "RESET" ? { type: "idle" } : state;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Type Inference Techniques
|
||||
|
||||
### 1. Infer Keyword
|
||||
|
||||
```typescript
|
||||
// Extract array element type
|
||||
type ElementType<T> = T extends (infer U)[] ? U : never;
|
||||
|
||||
type NumArray = number[];
|
||||
type Num = ElementType<NumArray>; // number
|
||||
|
||||
// Extract promise type
|
||||
type PromiseType<T> = T extends Promise<infer U> ? U : never;
|
||||
|
||||
type AsyncNum = PromiseType<Promise<number>>; // number
|
||||
|
||||
// Extract function parameters
|
||||
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
|
||||
|
||||
function foo(a: string, b: number) {}
|
||||
type FooParams = Parameters<typeof foo>; // [string, number]
|
||||
```
|
||||
|
||||
### 2. Type Guards
|
||||
|
||||
```typescript
|
||||
function isString(value: unknown): value is string {
|
||||
return typeof value === "string";
|
||||
}
|
||||
|
||||
function isArrayOf<T>(
|
||||
value: unknown,
|
||||
guard: (item: unknown) => item is T
|
||||
): value is T[] {
|
||||
return Array.isArray(value) && value.every(guard);
|
||||
}
|
||||
|
||||
const data: unknown = ["a", "b", "c"];
|
||||
|
||||
if (isArrayOf(data, isString)) {
|
||||
data.forEach(s => s.toUpperCase()); // Type: string[]
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Assertion Functions
|
||||
|
||||
```typescript
|
||||
function assertIsString(value: unknown): asserts value is string {
|
||||
if (typeof value !== "string") {
|
||||
throw new Error("Not a string");
|
||||
}
|
||||
}
|
||||
|
||||
function processValue(value: unknown) {
|
||||
assertIsString(value);
|
||||
// value is now typed as string
|
||||
console.log(value.toUpperCase());
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use `unknown` over `any`**: Enforce type checking
|
||||
2. **Prefer `interface` for object shapes**: Better error messages
|
||||
3. **Use `type` for unions and complex types**: More flexible
|
||||
4. **Leverage type inference**: Let TypeScript infer when possible
|
||||
5. **Create helper types**: Build reusable type utilities
|
||||
6. **Use const assertions**: Preserve literal types
|
||||
7. **Avoid type assertions**: Use type guards instead
|
||||
8. **Document complex types**: Add JSDoc comments
|
||||
9. **Use strict mode**: Enable all strict compiler options
|
||||
10. **Test your types**: Use type tests to verify type behavior
|
||||
|
||||
## Type Testing
|
||||
|
||||
```typescript
|
||||
// Type assertion tests
|
||||
type AssertEqual<T, U> =
|
||||
[T] extends [U]
|
||||
? [U] extends [T]
|
||||
? true
|
||||
: false
|
||||
: false;
|
||||
|
||||
type Test1 = AssertEqual<string, string>; // true
|
||||
type Test2 = AssertEqual<string, number>; // false
|
||||
type Test3 = AssertEqual<string | number, string>; // false
|
||||
|
||||
// Expect error helper
|
||||
type ExpectError<T extends never> = T;
|
||||
|
||||
// Example usage
|
||||
type ShouldError = ExpectError<AssertEqual<string, number>>;
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
1. **Over-using `any`**: Defeats the purpose of TypeScript
|
||||
2. **Ignoring strict null checks**: Can lead to runtime errors
|
||||
3. **Too complex types**: Can slow down compilation
|
||||
4. **Not using discriminated unions**: Misses type narrowing opportunities
|
||||
5. **Forgetting readonly modifiers**: Allows unintended mutations
|
||||
6. **Circular type references**: Can cause compiler errors
|
||||
7. **Not handling edge cases**: Like empty arrays or null values
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- Avoid deeply nested conditional types
|
||||
- Use simple types when possible
|
||||
- Cache complex type computations
|
||||
- Limit recursion depth in recursive types
|
||||
- Use build tools to skip type checking in production
|
||||
|
||||
## Resources
|
||||
|
||||
- **TypeScript Handbook**: https://www.typescriptlang.org/docs/handbook/
|
||||
- **Type Challenges**: https://github.com/type-challenges/type-challenges
|
||||
- **TypeScript Deep Dive**: https://basarat.gitbook.io/typescript/
|
||||
- **Effective TypeScript**: Book by Dan Vanderkam
|
||||
Reference in New Issue
Block a user