| name | moai-lang-typescript |
| version | 2.0.0 |
| created | Thu Nov 06 2025 00:00:00 GMT+0000 (Coordinated Universal Time) |
| updated | Thu Nov 06 2025 00:00:00 GMT+0000 (Coordinated Universal Time) |
| status | active |
| description | TypeScript best practices with modern frameworks, full-stack development, and type-safe patterns for 2025 |
| keywords | typescript, javascript, frontend, backend, fullstack, react, nextjs, nodejs, type-safety |
| allowed-tools | Read, Write, Edit, Bash, WebFetch, WebSearch |
TypeScript Development Mastery
Modern TypeScript Development with 2025 Best Practices
Comprehensive TypeScript development guidance covering full-stack applications, type-safe patterns, modern frameworks, and production-ready code using the latest tools and methodologies.
What It Does
- Full-Stack Development: Next.js, React, Node.js with end-to-end type safety
- Type-Safe APIs: REST and GraphQL with TypeScript validation
- Modern Frontend: React 19+, Vue 3+, Angular 17+ with TypeScript
- Backend Services: Node.js, Deno, Bun with TypeScript optimization
- Testing Strategies: Vitest, Jest, Playwright with type-safe testing
- Build & Deployment: Modern bundlers, CI/CD, performance optimization
- Code Quality: ESLint, Prettier, automated type checking
- Enterprise Patterns: Monorepos, microservices, scalable architecture
When to Use
Perfect Scenarios
- Building full-stack web applications
- Developing type-safe APIs and microservices
- Creating modern React/Next.js applications
- Enterprise-scale JavaScript applications
- Projects requiring strict type safety
- Teams with multiple JavaScript skill levels
- Applications with complex data models
Common Triggers
- "Create TypeScript API"
- "Set up Next.js project"
- "Type-safe React components"
- "TypeScript best practices"
- "Migrate JavaScript to TypeScript"
- "Full-stack TypeScript application"
Tool Version Matrix (2025-11-06)
Core TypeScript
- TypeScript: 5.6.x (latest) / 5.3.x (LTS)
- Node.js: 22.x (LTS) / 20.x (stable)
- Package Managers: pnpm 9.x, npm 10.x, yarn 4.x
- Runtimes: Node.js 22.x, Deno 2.x, Bun 1.1.x
Frontend Frameworks
- Next.js: 15.x - Full-stack React framework
- React: 19.x - UI library with Server Components
- Vue: 3.5.x - Progressive framework
- Angular: 18.x - Enterprise framework
- Svelte: 5.x - Compiler-based framework
Build Tools
- Vite: 6.x - Fast build tool
- Webpack: 5.x - Module bundler
- esbuild: 0.24.x - Fast bundler
- SWC: 1.7.x - Rust-based compiler
Testing Tools
- Vitest: 2.x - Fast unit testing
- Jest: 30.x - Comprehensive testing
- Playwright: 1.48.x - E2E testing
- Testing Library: 16.x - Component testing
Code Quality
- ESLint: 9.x - Linting (flat config)
- Prettier: 3.3.x - Code formatting
- TypeScript Compiler: Built-in type checking
- Husky: 9.x - Git hooks
Backend Frameworks
- Express: 4.21.x - Minimal web framework
- Fastify: 5.x - Fast web framework
- NestJS: 10.x - Enterprise framework
- tRPC: 11.x - End-to-end typesafe APIs
Ecosystem Overview
Project Setup (2025 Best Practice)
# Modern TypeScript with pnpm (recommended)
pnpm create next-app@latest my-app --typescript --tailwind --eslint --app
cd my-app
pnpm add @types/node @types/react @types/react-dom
pnpm add -D vitest @vitest/ui jsdom @testing-library/react
# Alternative: Bun for ultra-fast development
bun create next-app my-app
cd my-app
bun add vitest @testing-library/react -d
# Monorepo with Nx or Turborepo
npx create-nx-workspace@latest my-workspace --preset=react-monorepo
# or
npx create-turbo@latest my-turbo-app
Modern Project Structure
my-typescript-project/
├── package.json
├── tsconfig.json # TypeScript configuration
├── tsconfig.build.json # Build-specific config
├── vite.config.ts # Build tool configuration
├── eslint.config.js # ESLint flat config
├── prettier.config.js # Prettier configuration
├── playwright.config.ts # E2E testing config
├── vitest.config.ts # Unit testing config
├── src/
│ ├── app/ # Next.js app router
│ │ ├── layout.tsx
│ │ ├── page.tsx
│ │ ├── globals.css
│ │ └── api/ # API routes
│ ├── components/ # Reusable components
│ │ ├── ui/ # UI components
│ │ └── features/ # Feature components
│ ├── lib/ # Utilities and configs
│ │ ├── utils.ts
│ │ ├── db.ts # Database configuration
│ │ └── types.ts # Global type definitions
│ ├── hooks/ # Custom React hooks
│ └── types/ # Domain-specific types
├── tests/
│ ├── __mocks__/ # Mock files
│ ├── setup.ts # Test setup
│ └── integration/ # Integration tests
└── .github/
└── workflows/ # CI/CD pipelines
Modern TypeScript Patterns
Advanced Type System Features (TypeScript 5.6)
// Import attributes for better module management
import data from "./data.json" with { type: "json" };
// Template literal type improvements
type EventName<T extends string> = `on${Capitalize<T>}`;
type UserEvent = EventName<'click' | 'hover'>; // 'onClick' | 'onHover'
// Enhanced symbol key handling
const metaKey = Symbol('metadata');
interface Metadata {
[metaKey]: string;
}
// Improved decorator metadata
function Entity<T extends { new(...args: any[]): {} }>(constructor: T) {
return class extends constructor {
createdAt = new Date();
};
}
@Entity
class User {
constructor(public name: string) {}
}
Utility Types and Patterns
// Advanced utility types
type StrictOmit<T, K extends keyof T> = T extends Record<string, unknown>
? Pick<T, Exclude<keyof T, K>>
: never;
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
type Branded<T, B> = T & { __brand: B };
// Type-safe IDs
type UserId = Branded<string, 'UserId'>;
type ProductId = Branded<string, 'ProductId'>;
function createUserId(id: string): UserId {
return id as UserId;
}
// Type-safe API client
type ApiResponse<T, E = unknown> =
| { success: true; data: T }
| { success: false; error: E };
interface User {
id: UserId;
name: string;
email: string;
}
async function fetchUser(id: UserId): Promise<ApiResponse<User>> {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) throw new Error('Failed to fetch');
const data = await response.json();
return { success: true, data };
} catch (error) {
return { success: false, error };
}
}
React Patterns with TypeScript
// Type-safe component props with forwardRef
import React, { forwardRef, useImperativeHandle } from 'react';
interface ButtonProps {
variant: 'primary' | 'secondary' | 'danger';
size: 'sm' | 'md' | 'lg';
disabled?: boolean;
children: React.ReactNode;
onClick?: () => void;
}
export interface ButtonRef {
click: () => void;
focus: () => void;
}
export const Button = forwardRef<ButtonRef, ButtonProps>(
({ variant, size, disabled, children, onClick }, ref) => {
const buttonRef = React.useRef<HTMLButtonElement>(null);
useImperativeHandle(ref, () => ({
click: () => buttonRef.current?.click(),
focus: () => buttonRef.current?.focus(),
}));
const baseClasses = 'font-semibold rounded-lg transition-colors';
const variantClasses = {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
danger: 'bg-red-600 text-white hover:bg-red-700',
};
const sizeClasses = {
sm: 'px-3 py-1 text-sm',
md: 'px-4 py-2 text-base',
lg: 'px-6 py-3 text-lg',
};
return (
<button
ref={buttonRef}
className={`${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]}`}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);
}
);
Button.displayName = 'Button';
Custom Hooks with TypeScript
// Type-safe data fetching hook
interface UseApiResult<T, E = unknown> {
data: T | null;
error: E | null;
loading: boolean;
refetch: () => Promise<void>;
}
function useApi<T, E = unknown>(
url: string,
options?: RequestInit
): UseApiResult<T, E> {
const [data, setData] = React.useState<T | null>(null);
const [error, setError] = React.useState<E | null>(null);
const [loading, setLoading] = React.useState(false);
const fetchData = React.useCallback(async () => {
setLoading(true);
setError(null);
try {
const response = await fetch(url, options);
if (!response.ok) throw new Error(response.statusText);
const result = await response.json() as T;
setData(result);
} catch (err) {
setError(err as E);
} finally {
setLoading(false);
}
}, [url, options]);
React.useEffect(() => {
fetchData();
}, [fetchData]);
return { data, error, loading, refetch: fetchData };
}
// Usage
interface User {
id: number;
name: string;
email: string;
}
function UserProfile({ userId }: { userId: number }) {
const { data: user, loading, error } = useApi<User>(`/api/users/${userId}`);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
if (!user) return <div>No user found</div>;
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
Performance Optimization
Code Splitting and Lazy Loading
// Dynamic imports with type safety
import { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() =>
import('./HeavyComponent').then(module => ({
default: module.HeavyComponent
}))
);
// Route-based code splitting
import { createBrowserRouter } from 'react-router-dom';
const router = createBrowserRouter([
{
path: '/',
element: <HomePage />,
},
{
path: '/dashboard',
element: (
<Suspense fallback={<div>Loading dashboard...</div>}>
<LazyDashboard />
</Suspense>
),
loader: () => import('./loaders/dashboardLoader').then(m => m.dashboardLoader()),
},
]);
// Preloading strategies
const preloadComponent = () => {
import('./HeavyComponent');
};
// Preload on hover
<button onMouseEnter={preloadComponent}>
Load Component
</button>
Bundle Optimization
// vite.config.ts - Modern build configuration
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc';
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [
react(),
process.env.ANALYZE && visualizer({
filename: 'dist/stats.html',
open: true,
}),
],
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
router: ['react-router-dom'],
ui: ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu'],
},
},
},
chunkSizeWarningLimit: 1000,
},
server: {
hmr: {
overlay: true,
},
},
});
Memory Management
// Efficient state management with useReducer
interface State {
items: Item[];
filter: string;
loading: boolean;
}
type Action =
| { type: 'SET_ITEMS'; payload: Item[] }
| { type: 'SET_FILTER'; payload: string }
| { type: 'SET_LOADING'; payload: boolean };
function itemsReducer(state: State, action: Action): State {
switch (action.type) {
case 'SET_ITEMS':
return { ...state, items: action.payload, loading: false };
case 'SET_FILTER':
return { ...state, filter: action.payload };
case 'SET_LOADING':
return { ...state, loading: action.payload };
default:
return state;
}
}
// Memoized components for performance
import { memo, useMemo, useCallback } from 'react';
interface ExpensiveComponentProps {
data: number[];
onItemClick: (id: number) => void;
}
const ExpensiveComponent = memo<ExpensiveComponentProps>(({ data, onItemClick }) => {
const processedData = useMemo(() => {
return data.map(item => ({
...item,
processed: expensiveCalculation(item),
}));
}, [data]);
const handleClick = useCallback((id: number) => {
onItemClick(id);
}, [onItemClick]);
return (
<div>
{processedData.map(item => (
<Item key={item.id} item={item} onClick={handleClick} />
))}
</div>
);
});
Testing Strategies
Vitest Configuration
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react-swc';
import path from 'path';
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['./tests/setup.ts'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: [
'node_modules/',
'tests/',
'**/*.d.ts',
'**/*.config.*',
'dist/',
],
},
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});
Type-Safe Testing Patterns
// Component testing with Testing Library
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { vi, type MockedFunction } from 'vitest';
import { Button } from './Button';
describe('Button', () => {
it('renders with correct variant styles', () => {
render(
<Button variant="primary" size="md" onClick={vi.fn()}>
Click me
</Button>
);
const button = screen.getByRole('button', { name: 'Click me' });
expect(button).toBeInTheDocument();
expect(button).toHaveClass('bg-blue-600', 'text-white');
});
it('handles click events', async () => {
const handleClick = vi.fn();
render(
<Button variant="secondary" size="sm" onClick={handleClick}>
Submit
</Button>
);
const button = screen.getByRole('button', { name: 'Submit' });
fireEvent.click(button);
expect(handleClick).toHaveBeenCalledTimes(1);
});
it('can be triggered via ref', () => {
const handleClick = vi.fn();
const ref = { current: null };
render(
<Button variant="primary" size="md" onClick={handleClick} ref={ref}>
Click via ref
</Button>
);
// TypeScript should infer the correct ref type
if (ref.current) {
ref.current.click();
}
expect(handleClick).toHaveBeenCalledTimes(1);
});
});
// API testing with MSW
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import { fetchUser } from './api';
const server = setupServer(
rest.get('/api/users/1', (req, res, ctx) => {
return res(
ctx.json({
id: 1,
name: 'John Doe',
email: 'john@example.com',
})
);
})
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
describe('fetchUser', () => {
it('fetches user data successfully', async () => {
const userId = createUserId('1');
const result = await fetchUser(userId);
if (result.success) {
expect(result.data.name).toBe('John Doe');
expect(result.data.email).toBe('john@example.com');
} else {
fail('Expected successful fetch');
}
});
});
E2E Testing with Playwright
// tests/e2e/user-journey.spec.ts
import { test, expect, type Page } from '@playwright/test';
test.describe('User Authentication Flow', () => {
test('should allow user to sign up and log in', async ({ page }) => {
// Sign up
await page.goto('/signup');
await page.fill('[data-testid=email-input]', 'user@example.com');
await page.fill('[data-testid=password-input]', 'securepassword123');
await page.fill('[data-testid=name-input]', 'Test User');
await page.click('[data-testid=signup-button]');
// Should redirect to dashboard
await expect(page).toHaveURL('/dashboard');
await expect(page.locator('[data-testid=user-name]')).toHaveText('Test User');
// Log out
await page.click('[data-testid=logout-button]');
// Log in
await page.goto('/login');
await page.fill('[data-testid=email-input]', 'user@example.com');
await page.fill('[data-testid=password-input]', 'securepassword123');
await page.click('[data-testid=login-button]');
// Should be back at dashboard
await expect(page).toHaveURL('/dashboard');
});
test('should show validation errors for invalid input', async ({ page }) => {
await page.goto('/signup');
// Try to submit with empty fields
await page.click('[data-testid=signup-button]');
await expect(page.locator('[data-testid=email-error]')).toBeVisible();
await expect(page.locator('[data-testid=password-error]')).toBeVisible();
await expect(page.locator('[data-testid=name-error]')).toBeVisible();
});
});
Security Best Practices
Input Validation and Sanitization
// Type-safe validation with Zod
import { z } from 'zod';
const UserSchema = z.object({
name: z.string().min(1).max(100).trim(),
email: z.string().email(),
age: z.number().min(13).max(120),
bio: z.string().max(1000).optional(),
});
type User = z.infer<typeof UserSchema>;
// API route validation
export async function POST(request: Request) {
try {
const body = await request.json();
const validatedUser = UserSchema.parse(body);
// Process validated data
await createUser(validatedUser);
return Response.json({ success: true }, { status: 201 });
} catch (error) {
if (error instanceof z.ZodError) {
return Response.json(
{
success: false,
errors: error.flatten().fieldErrors
},
{ status: 400 }
);
}
return Response.json(
{ success: false, error: 'Internal server error' },
{ status: 500 }
);
}
}
// XSS prevention
function sanitizeHtml(input: string): string {
// Use DOMPurify or similar library
return input.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
}
// CSRF protection
function getCSRFToken(): string {
const meta = document.querySelector('meta[name="csrf-token"]');
return meta?.getAttribute('content') || '';
}
// Secure API client
class SecureApiClient {
private baseUrl: string;
private csrfToken: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
this.csrfToken = getCSRFToken();
}
async post<T>(endpoint: string, data: unknown): Promise<T> {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': this.csrfToken,
},
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json() as Promise<T>;
}
}
Authentication and Authorization
// JWT-based authentication
interface JWTPayload {
userId: string;
email: string;
role: 'user' | 'admin';
exp: number;
}
class AuthService {
private static readonly TOKEN_KEY = 'auth_token';
static setToken(token: string): void {
localStorage.setItem(this.TOKEN_KEY, token);
}
static getToken(): string | null {
return localStorage.getItem(this.TOKEN_KEY);
}
static removeToken(): void {
localStorage.removeItem(this.TOKEN_KEY);
}
static isTokenExpired(token: string): boolean {
try {
const payload = JSON.parse(atob(token.split('.')[1])) as JWTPayload;
return Date.now() >= payload.exp * 1000;
} catch {
return true;
}
}
static getCurrentUser(): JWTPayload | null {
const token = this.getToken();
if (!token || this.isTokenExpired(token)) {
return null;
}
try {
return JSON.parse(atob(token.split('.')[1])) as JWTPayload;
} catch {
return null;
}
}
}
// Protected routes
interface ProtectedRouteProps {
children: React.ReactNode;
requiredRole?: 'user' | 'admin';
}
function ProtectedRoute({ children, requiredRole = 'user' }: ProtectedRouteProps) {
const user = AuthService.getCurrentUser();
if (!user) {
return <Navigate to="/login" replace />;
}
if (requiredRole === 'admin' && user.role !== 'admin') {
return <Navigate to="/unauthorized" replace />;
}
return <>{children}</>;
}
// API middleware
function withAuth<T extends unknown[], R>(
handler: (...args: T) => Promise<R>
) {
return async (...args: T): Promise<R> => {
const token = AuthService.getToken();
if (!token || AuthService.isTokenExpired(token)) {
throw new Error('Unauthorized');
}
return handler(...args);
};
}
Integration Patterns
Full-Stack Type Safety with tRPC
// server/router.ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
import type { CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';
const t = initTRPC.context<CreateHTTPContextOptions>().create();
export const appRouter = t.router({
getUser: t.procedure
.input(z.object({ id: z.string() }))
.query(async ({ input, ctx }) => {
const user = await ctx.db.user.findUnique({
where: { id: input.id },
});
return user;
}),
createUser: t.procedure
.input(z.object({
name: z.string().min(1),
email: z.string().email(),
}))
.mutation(async ({ input, ctx }) => {
const user = await ctx.db.user.create({
data: input,
});
return user;
}),
});
export type AppRouter = typeof appRouter;
// client/trpc.ts
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from '../server/router';
const trpc = createTRPCProxyClient<AppRouter>({
links: [
httpBatchLink({
url: 'http://localhost:3000/trpc',
}),
],
});
// Usage in React component
function UserProfile({ userId }: { userId: string }) {
const { data: user, isLoading } = trpc.getUser.useQuery({ id: userId });
const createUser = trpc.createUser.useMutation();
if (isLoading) return <div>Loading...</div>;
return (
<div>
<h1>{user?.name}</h1>
<p>{user?.email}</p>
<button
onClick={() => createUser.mutate({
name: 'New User',
email: 'new@example.com',
})}
>
Create User
</button>
</div>
);
}
Database Integration with Prisma
// schema.prisma
model User {
id String @id @default(cuid())
email String @unique
name String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
posts Post[]
}
model Post {
id String @id @default(cuid())
title String
content String
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// lib/db.ts
import { PrismaClient } from '@prisma/client';
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const prisma = globalForPrisma.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
// API routes with type safety
import { z } from 'zod';
const createPostSchema = z.object({
title: z.string().min(1).max(200),
content: z.string().min(1),
authorId: z.string().cuid(),
});
export async function POST(request: Request) {
try {
const body = await request.json();
const { title, content, authorId } = createPostSchema.parse(body);
const post = await prisma.post.create({
data: {
title,
content,
authorId,
},
include: {
author: true,
},
});
return Response.json(post, { status: 201 });
} catch (error) {
if (error instanceof z.ZodError) {
return Response.json(
{ errors: error.flatten().fieldErrors },
{ status: 400 }
);
}
return Response.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}
State Management with Zustand
// store/userStore.ts
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
interface User {
id: string;
name: string;
email: string;
}
interface UserStore {
user: User | null;
isLoading: boolean;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
updateProfile: (updates: Partial<User>) => void;
}
export const useUserStore = create<UserStore>()(
devtools(
persist(
(set, get) => ({
user: null,
isLoading: false,
login: async (email: string, password: string) => {
set({ isLoading: true });
try {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
if (response.ok) {
const user = await response.json();
set({ user, isLoading: false });
} else {
throw new Error('Login failed');
}
} catch (error) {
set({ isLoading: false });
throw error;
}
},
logout: () => {
set({ user: null });
},
updateProfile: (updates) => {
const currentUser = get().user;
if (currentUser) {
set({ user: { ...currentUser, ...updates } });
}
},
}),
{
name: 'user-storage',
partialize: (state) => ({ user: state.user }),
}
)
)
);
// Usage in components
function ProfilePage() {
const { user, isLoading, updateProfile } = useUserStore();
if (isLoading) return <div>Loading...</div>;
if (!user) return <div>Please log in</div>;
return (
<div>
<h1>Profile</h1>
<p>Name: {user.name}</p>
<p>Email: {user.email}</p>
<button
onClick={() => updateProfile({ name: 'Updated Name' })}
>
Update Name
</button>
</div>
);
}
Modern Development Workflow
Configuration Files
// package.json
{
"name": "my-typescript-app",
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "eslint . --max-warnings 0",
"lint:fix": "eslint . --fix",
"type-check": "tsc --noEmit",
"test": "vitest",
"test:ui": "vitest --ui",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui"
},
"dependencies": {
"next": "^15.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"@trpc/client": "^11.0.0",
"@trpc/server": "^11.0.0",
"@trpc/react-query": "^11.0.0",
"zod": "^3.23.0",
"zustand": "^5.0.0"
},
"devDependencies": {
"@types/node": "^22.0.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"typescript": "^5.6.0",
"eslint": "^9.0.0",
"prettier": "^3.3.0",
"vitest": "^2.0.0",
"@testing-library/react": "^16.0.0",
"playwright": "^1.48.0"
}
}
// tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"lib": ["dom", "dom.iterable", "ES2022"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "ESNext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"@/components/*": ["./src/components/*"],
"@/lib/*": ["./src/lib/*"],
"@/types/*": ["./src/types/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
// eslint.config.js (ESLint Flat Config)
import js from '@eslint/js';
import typescript from '@typescript-eslint/eslint-plugin';
import typescriptParser from '@typescript-eslint/parser';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
export default [
js.configs.recommended,
{
files: ['**/*.{ts,tsx}'],
languageOptions: {
parser: typescriptParser,
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
},
},
plugins: {
'@typescript-eslint': typescript,
'react': react,
'react-hooks': reactHooks,
},
rules: {
...typescript.configs.recommended.rules,
'@typescript-eslint/no-unused-vars': 'error',
'@typescript-eslint/no-explicit-any': 'warn',
'react/react-in-jsx-scope': 'off',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
},
settings: {
react: {
version: 'detect',
},
},
},
];
CI/CD Pipeline
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 9
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Type check
run: pnpm type-check
- name: Lint
run: pnpm lint
- name: Test
run: pnpm test
- name: E2E tests
run: pnpm test:e2e
- name: Build
run: pnpm build
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
file: ./coverage/lcov.info
Created by: MoAI Language Skill Factory
Last Updated: 2025-11-06
Version: 2.0.0
TypeScript Target: 5.6+ with latest language features
This skill provides comprehensive TypeScript development guidance with 2025 best practices, covering everything from basic type safety to advanced full-stack patterns and enterprise-grade applications.