Framework Integration Patterns

1. React TypeScript Patterns and Component Typing

Component Type Syntax Description Use Case
Function Component React.FC<Props> Function component with props type - includes children implicitly Simple components, hooks-based
Function Component (Direct) (props: Props) => JSX.Element Direct function typing - more explicit, no implicit children Preferred modern approach
Props Interface interface Props { } Define component props with TypeScript interface Type-safe prop passing
Children Prop React.ReactNode Type for React children - elements, strings, numbers, fragments Components accepting children
Event Handlers React.MouseEvent<T> Typed DOM events specific to React onClick, onChange, onSubmit
Refs React.RefObject<T> Typed reference to DOM elements or component instances Direct DOM access, imperative APIs
Hooks useState<T>() Generic hooks with type parameters Typed state management

Example: Function components with props

import React from 'react';

// Props interface
interface ButtonProps {
  text: string;
  onClick: () => void;
  disabled?: boolean;
  variant?: 'primary' | 'secondary';
}

// Modern approach - direct function typing
function Button({ text, onClick, disabled, variant = 'primary' }: ButtonProps) {
  return (
    <button
      onClick={onClick}
      disabled={disabled}
      className={`btn-${variant}`}
    >
      {text}
    </button>
  );
}

// With children
interface CardProps {
  title: string;
  children: React.ReactNode;
}

function Card({ title, children }: CardProps) {
  return (
    <div className="card">
      <h2>{title}</h2>
      <div className="card-body">{children}</div>
    </div>
  );
}

// Generic component
interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
}

function List<T>({ items, renderItem }: ListProps<T>) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{renderItem(item)}</li>
      ))}
    </ul>
  );
}

Example: Event handlers and refs

import React, { useState, useRef } from 'react';

interface FormProps {
  onSubmit: (data: FormData) => void;
}

interface FormData {
  username: string;
  email: string;
}

function Form({ onSubmit }: FormProps) {
  const [formData, setFormData] = useState<FormData>({
    username: '',
    email: ''
  });
  
  // Typed ref
  const inputRef = useRef<HTMLInputElement>(null);
  
  // Typed event handler
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setFormData(prev => ({ ...prev, [name]: value }));
  };
  
  // Form submit handler
  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    onSubmit(formData);
  };
  
  // Button click handler
  const handleFocus = (e: React.MouseEvent<HTMLButtonElement>) => {
    inputRef.current?.focus();
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        ref={inputRef}
        type="text"
        name="username"
        value={formData.username}
        onChange={handleChange}
      />
      <input
        type="email"
        name="email"
        value={formData.email}
        onChange={handleChange}
      />
      <button type="button" onClick={handleFocus}>Focus Input</button>
      <button type="submit">Submit</button>
    </form>
  );
}

Example: Hooks with TypeScript

import { useState, useEffect, useReducer, useContext } from 'react';

// useState with explicit type
const [count, setCount] = useState<number>(0);
const [user, setUser] = useState<User | null>(null);

// useState with type inference
const [name, setName] = useState(''); // string inferred

// useReducer with types
type State = { count: number };
type Action = { type: 'increment' } | { type: 'decrement' } | { type: 'reset' };

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case 'increment': return { count: state.count + 1 };
    case 'decrement': return { count: state.count - 1 };
    case 'reset': return { count: 0 };
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });
  
  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </div>
  );
}

// useContext with types
interface ThemeContextType {
  theme: 'light' | 'dark';
  toggleTheme: () => void;
}

const ThemeContext = React.createContext<ThemeContextType | undefined>(undefined);

function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within ThemeProvider');
  }
  return context;
}

// Custom hook with generics
function useFetch<T>(url: string) {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);
  
  useEffect(() => {
    fetch(url)
      .then(res => res.json())
      .then((data: T) => setData(data))
      .catch(err => setError(err))
      .finally(() => setLoading(false));
  }, [url]);
  
  return { data, loading, error };
}

2. Vue.js TypeScript Integration and Composition API

Feature Syntax Description Use Case
defineComponent defineComponent({ }) Define component with TypeScript inference Options API with types
Ref<T> ref<T>(value) Typed reactive reference Reactive primitive values
Reactive<T> reactive<T>(obj) Typed reactive object Reactive complex objects
Computed<T> computed<T>(() => T) Typed computed property Derived reactive state
PropType<T> type: Object as PropType<T> Type assertion for complex props Object/array props
EmitsOptions emits: { eventName: (payload: T) => boolean } Typed event emissions Component events

Example: Vue 3 Composition API with TypeScript

<script setup lang="ts">
import { ref, reactive, computed, watch } from 'vue';

// Typed refs
const count = ref<number>(0);
const message = ref<string>('Hello');

// Typed reactive object
interface User {
  name: string;
  email: string;
  age: number;
}

const user = reactive<User>({
  name: 'John',
  email: 'john@example.com',
  age: 30
});

// Computed with type inference
const doubleCount = computed(() => count.value * 2);

// Explicit computed type
const displayName = computed<string>(() => {
  return `${user.name} (${user.age})`;
});

// Watch with types
watch(count, (newVal: number, oldVal: number) => {
  console.log(`Count changed from ${oldVal} to ${newVal}`);
});

// Function with types
function increment(): void {
  count.value++;
}

function updateUser(updates: Partial<User>): void {
  Object.assign(user, updates);
}
</script>

<template>
  <div>
    <p>{{ message }}</p>
    <p>Count: {{ count }} (Double: {{ doubleCount }})</p>
    <p>{{ displayName }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

Example: Props and emits with types

<script setup lang="ts">
import { PropType } from 'vue';

// Define props interface
interface User {
  id: number;
  name: string;
  email: string;
}

// Props with types
const props = defineProps({
  title: {
    type: String,
    required: true
  },
  count: {
    type: Number,
    default: 0
  },
  user: {
    type: Object as PropType<User>,
    required: true
  },
  tags: {
    type: Array as PropType<string[]>,
    default: () => []
  }
});

// Typed emits
const emit = defineEmits<{
  (e: 'update', value: number): void;
  (e: 'delete', id: number): void;
  (e: 'change', user: User): void;
}>();

// Or with validation
const emit = defineEmits({
  update: (value: number) => value >= 0,
  delete: (id: number) => typeof id === 'number',
  change: (user: User) => user.email.includes('@')
});

// Use emits
function handleUpdate() {
  emit('update', props.count + 1);
}

function handleDelete() {
  emit('delete', props.user.id);
}
</script>

3. Angular TypeScript Features and Decorators

Decorator Target Description Example
@Component Class Define Angular component with metadata @Component({ selector: 'app-root' })
@Injectable Class Mark class as available for dependency injection @Injectable({ providedIn: 'root' })
@Input Property Define input property for component @Input() name: string;
@Output Property Define output event emitter @Output() change = new EventEmitter();
@ViewChild Property Query for child element or component @ViewChild('input') inputEl!: ElementRef;
@HostListener Method Listen to host element events @HostListener('click')

Example: Angular component with TypeScript

import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';

// Interface for component data
interface User {
  id: number;
  name: string;
  email: string;
}

@Component({
  selector: 'app-user-card',
  template: `
    <div class="card">
      <h3>{{ user.name }}</h3>
      <p>{{ user.email }}</p>
      <button (click)="handleEdit()">Edit</button>
      <button (click)="handleDelete()">Delete</button>
    </div>
  `,
  styleUrls: ['./user-card.component.css']
})
export class UserCardComponent implements OnInit {
  // Input with type
  @Input() user!: User;
  @Input() editable: boolean = true;
  
  // Output with typed EventEmitter
  @Output() edit = new EventEmitter<User>();
  @Output() delete = new EventEmitter<number>();
  
  ngOnInit(): void {
    console.log('User card initialized', this.user);
  }
  
  handleEdit(): void {
    if (this.editable) {
      this.edit.emit(this.user);
    }
  }
  
  handleDelete(): void {
    this.delete.emit(this.user.id);
  }
}

Example: Angular service with dependency injection

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map, catchError } from 'rxjs/operators';

interface User {
  id: number;
  name: string;
  email: string;
}

interface ApiResponse<T> {
  data: T;
  status: number;
}

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private apiUrl = 'https://api.example.com';
  
  constructor(private http: HttpClient) {}
  
  getUsers(): Observable<User[]> {
    return this.http.get<ApiResponse<User[]>>(`${this.apiUrl}/users`)
      .pipe(
        map(response => response.data),
        catchError(this.handleError)
      );
  }
  
  getUser(id: number): Observable<User> {
    return this.http.get<ApiResponse<User>>(`${this.apiUrl}/users/${id}`)
      .pipe(map(response => response.data));
  }
  
  createUser(user: Omit<User, 'id'>): Observable<User> {
    return this.http.post<ApiResponse<User>>(`${this.apiUrl}/users`, user)
      .pipe(map(response => response.data));
  }
  
  updateUser(id: number, updates: Partial<User>): Observable<User> {
    return this.http.patch<ApiResponse<User>>(`${this.apiUrl}/users/${id}`, updates)
      .pipe(map(response => response.data));
  }
  
  private handleError(error: any): Observable<never> {
    console.error('API Error:', error);
    throw error;
  }
}

4. Node.js TypeScript Development and @types/node

Feature Package/Type Description Use Case
@types/node npm i -D @types/node TypeScript definitions for Node.js built-in modules Core Node.js development
Process NodeJS.Process Type for process object - env, argv, exit, etc. Environment variables, CLI args
Buffer Buffer Binary data handling type File I/O, streams, binary data
EventEmitter EventEmitter Event-driven architecture base class Custom event systems
Stream Readable, Writable, Duplex Stream interface types Data streaming, pipes
Module Resolution "moduleResolution": "node16" Node.js-specific module resolution ESM and CommonJS interop

Example: Node.js modules with TypeScript

import * as fs from 'fs/promises';
import * as path from 'path';
import { EventEmitter } from 'events';
import { createReadStream, createWriteStream } from 'fs';

// File operations
async function readConfig(): Promise<Record<string, any>> {
  const configPath = path.join(__dirname, 'config.json');
  const content = await fs.readFile(configPath, 'utf-8');
  return JSON.parse(content);
}

async function writeLog(message: string): Promise<void> {
  const logPath = path.join(__dirname, 'app.log');
  const timestamp = new Date().toISOString();
  await fs.appendFile(logPath, `[${timestamp}] ${message}\n`);
}

// Environment variables with type safety
interface Env {
  NODE_ENV: 'development' | 'production' | 'test';
  PORT: string;
  DATABASE_URL: string;
  API_KEY: string;
}

function getEnv(): Env {
  return {
    NODE_ENV: (process.env.NODE_ENV as Env['NODE_ENV']) || 'development',
    PORT: process.env.PORT || '3000',
    DATABASE_URL: process.env.DATABASE_URL || '',
    API_KEY: process.env.API_KEY || ''
  };
}

// Streams with types
async function copyFile(source: string, dest: string): Promise<void> {
  return new Promise((resolve, reject) => {
    const readStream = createReadStream(source);
    const writeStream = createWriteStream(dest);
    
    readStream.on('error', reject);
    writeStream.on('error', reject);
    writeStream.on('finish', resolve);
    
    readStream.pipe(writeStream);
  });
}

// EventEmitter with types
interface DataEvents {
  data: (value: string) => void;
  error: (error: Error) => void;
  complete: () => void;
}

class DataProcessor extends EventEmitter {
  on<K extends keyof DataEvents>(event: K, listener: DataEvents[K]): this {
    return super.on(event, listener);
  }
  
  emit<K extends keyof DataEvents>(
    event: K,
    ...args: Parameters<DataEvents[K]>
  ): boolean {
    return super.emit(event, ...args);
  }
  
  process(data: string[]): void {
    data.forEach(item => {
      this.emit('data', item);
    });
    this.emit('complete');
  }
}

5. Express.js Type Safety and Middleware Typing

Type Syntax Description Use Case
Request<P, ResBody, ReqBody, Query> Request<ParamsDictionary, any, RequestBody> Typed Express request - params, body, query Type-safe route handlers
Response<ResBody> Response<ResponseBody> Typed response body Type-safe responses
NextFunction NextFunction Middleware next callback Middleware chain
RequestHandler<P, ResBody, ReqBody> RequestHandler<Params, Response, Body> Complete handler type with all generics Reusable typed handlers
ErrorRequestHandler (err, req, res, next) => void Error handling middleware type Global error handlers
Router Router() Express router with types Modular routes

Example: Express routes with TypeScript

import express, { Request, Response, NextFunction, RequestHandler } from 'express';

const app = express();
app.use(express.json());

// Interfaces for type safety
interface User {
  id: string;
  name: string;
  email: string;
}

interface CreateUserBody {
  name: string;
  email: string;
  password: string;
}

interface UpdateUserBody {
  name?: string;
  email?: string;
}

interface UserParams {
  userId: string;
}

interface UserQuery {
  page?: string;
  limit?: string;
}

// Typed route handlers
const getUsers: RequestHandler<{}, User[], {}, UserQuery> = async (req, res) => {
  const page = parseInt(req.query.page || '1');
  const limit = parseInt(req.query.limit || '10');
  
  const users = await fetchUsers(page, limit);
  res.json(users);
};

const getUser: RequestHandler<UserParams, User | { error: string }> = async (req, res) => {
  const { userId } = req.params;
  const user = await findUserById(userId);
  
  if (!user) {
    return res.status(404).json({ error: 'User not found' });
  }
  
  res.json(user);
};

const createUser: RequestHandler<{}, User, CreateUserBody> = async (req, res) => {
  const { name, email, password } = req.body;
  
  const newUser = await createNewUser({ name, email, password });
  res.status(201).json(newUser);
};

const updateUser: RequestHandler<UserParams, User, UpdateUserBody> = async (req, res) => {
  const { userId } = req.params;
  const updates = req.body;
  
  const updatedUser = await updateUserData(userId, updates);
  res.json(updatedUser);
};

// Routes
app.get('/users', getUsers);
app.get('/users/:userId', getUser);
app.post('/users', createUser);
app.patch('/users/:userId', updateUser);

Example: Typed middleware

import { Request, Response, NextFunction, RequestHandler } from 'express';

// Extend Express Request with custom properties
declare global {
  namespace Express {
    interface Request {
      user?: User;
      startTime?: number;
    }
  }
}

interface User {
  id: string;
  email: string;
  role: 'admin' | 'user';
}

// Authentication middleware
const authenticate: RequestHandler = async (req, res, next) => {
  try {
    const token = req.headers.authorization?.split(' ')[1];
    
    if (!token) {
      return res.status(401).json({ error: 'No token provided' });
    }
    
    const user = await verifyToken(token);
    req.user = user;
    next();
  } catch (error) {
    res.status(401).json({ error: 'Invalid token' });
  }
};

// Authorization middleware
function authorize(...roles: User['role'][]): RequestHandler {
  return (req, res, next) => {
    if (!req.user) {
      return res.status(401).json({ error: 'Not authenticated' });
    }
    
    if (!roles.includes(req.user.role)) {
      return res.status(403).json({ error: 'Insufficient permissions' });
    }
    
    next();
  };
}

// Logging middleware
const logger: RequestHandler = (req, res, next) => {
  req.startTime = Date.now();
  
  res.on('finish', () => {
    const duration = Date.now() - (req.startTime || 0);
    console.log(`${req.method} ${req.path} - ${res.statusCode} (${duration}ms)`);
  });
  
  next();
};

// Validation middleware
function validateBody<T>(schema: (body: any) => body is T): RequestHandler {
  return (req, res, next) => {
    if (!schema(req.body)) {
      return res.status(400).json({ error: 'Invalid request body' });
    }
    next();
  };
}

// Error handling middleware
const errorHandler: express.ErrorRequestHandler = (err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({
    error: 'Internal server error',
    message: err.message
  });
};

// Usage
app.use(logger);
app.get('/admin', authenticate, authorize('admin'), (req, res) => {
  res.json({ message: 'Admin only' });
});
app.use(errorHandler);

6. Next.js TypeScript Configuration and API Routes

Feature Type Description Use Case
GetStaticProps GetStaticProps<Props> Type for static generation function Build-time data fetching
GetServerSideProps GetServerSideProps<Props> Type for server-side rendering function Request-time data fetching
GetStaticPaths GetStaticPaths Type for dynamic route paths generation Dynamic static routes
NextApiRequest NextApiRequest API route request type API endpoints
NextApiResponse NextApiResponse<Data> API route response type Type-safe API responses
NextPage NextPage<Props> Page component type Next.js pages
AppProps AppProps<PageProps> _app.tsx component props type Custom App component

Example: Next.js pages with data fetching

import { GetStaticProps, GetServerSideProps, NextPage } from 'next';

// Page props interface
interface Post {
  id: number;
  title: string;
  content: string;
  author: string;
}

interface HomeProps {
  posts: Post[];
}

// Static generation
export const getStaticProps: GetStaticProps<HomeProps> = async (context) => {
  const res = await fetch('https://api.example.com/posts');
  const posts: Post[] = await res.json();
  
  return {
    props: {
      posts
    },
    revalidate: 60 // Revalidate every 60 seconds
  };
};

// Page component
const Home: NextPage<HomeProps> = ({ posts }) => {
  return (
    <div>
      <h1>Blog Posts</h1>
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.content}</p>
          <small>By {post.author}</small>
        </article>
      ))}
    </div>
  );
};

export default Home;

// Dynamic route with params
interface PostPageProps {
  post: Post;
}

export const getServerSideProps: GetServerSideProps<PostPageProps> = async (context) => {
  const { id } = context.params as { id: string };
  
  const res = await fetch(`https://api.example.com/posts/${id}`);
  const post: Post = await res.json();
  
  return {
    props: {
      post
    }
  };
};

Example: Next.js API routes

import type { NextApiRequest, NextApiResponse } from 'next';

// Response types
interface User {
  id: number;
  name: string;
  email: string;
}

interface ErrorResponse {
  error: string;
  details?: string;
}

type UserResponse = User | ErrorResponse;

// GET /api/users/[id]
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse<UserResponse>
) {
  const { id } = req.query;
  
  if (req.method !== 'GET') {
    return res.status(405).json({ error: 'Method not allowed' });
  }
  
  try {
    const user = await fetchUser(Number(id));
    
    if (!user) {
      return res.status(404).json({ error: 'User not found' });
    }
    
    res.status(200).json(user);
  } catch (error) {
    res.status(500).json({
      error: 'Internal server error',
      details: error instanceof Error ? error.message : 'Unknown error'
    });
  }
}

// POST /api/users - Create user
interface CreateUserBody {
  name: string;
  email: string;
}

export default async function createUserHandler(
  req: NextApiRequest,
  res: NextApiResponse<User | ErrorResponse>
) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }
  
  const { name, email } = req.body as CreateUserBody;
  
  if (!name || !email) {
    return res.status(400).json({ error: 'Name and email required' });
  }
  
  try {
    const newUser = await createUser({ name, email });
    res.status(201).json(newUser);
  } catch (error) {
    res.status(500).json({ error: 'Failed to create user' });
  }
}

// Middleware pattern for API routes
type ApiHandler<T = any> = (
  req: NextApiRequest,
  res: NextApiResponse<T>
) => Promise<void> | void;

function withAuth<T>(handler: ApiHandler<T>): ApiHandler<T | ErrorResponse> {
  return async (req, res) => {
    const token = req.headers.authorization;
    
    if (!token) {
      return res.status(401).json({ error: 'Unauthorized' });
    }
    
    try {
      await verifyToken(token);
      return handler(req, res);
    } catch (error) {
      return res.status(401).json({ error: 'Invalid token' });
    }
  };
}

// Protected route
export default withAuth<User>(async (req, res) => {
  const user = await getCurrentUser(req);
  res.status(200).json(user);
});
Note: Framework integration best practices:
  • React - Use direct function typing over React.FC, properly type hooks and event handlers
  • Vue 3 - Leverage <script setup lang="ts"> with Composition API for best inference
  • Angular - Enable strict mode, use interfaces for all data models, leverage RxJS types
  • Node.js - Install @types/node, use proper module resolution (node16/nodenext)
  • Express - Extend Express namespace for custom properties, type all middleware
  • Next.js - Use proper data fetching types, type API routes with NextApiRequest/Response

Framework Integration Summary

  • React - Type props, events, hooks, and refs with React.* types and generics
  • Vue 3 - Use defineProps, defineEmits, ref, reactive with proper type annotations
  • Angular - Leverage decorators (@Component, @Injectable) with typed services and RxJS
  • Node.js - Install @types/node for built-in modules, use proper async/stream types
  • Express - Type Request<P, ResBody, ReqBody, Query> and middleware with generics
  • Next.js - Use GetStaticProps, GetServerSideProps, and NextApiRequest types for data fetching and API routes