Back to Blog
Secure Coding in TypeScript: A Comprehensive Guide

Secure Coding in TypeScript: A Comprehensive Guide

Samet YamanTypeScript

Introduction

Ensuring security in TypeScript applications is crucial for protecting sensitive data, preventing vulnerabilities, and maintaining application integrity. In this guide, we will explore best practices for secure coding in TypeScript, covering key concepts, common pitfalls, and practical solutions.

1. Use Strict Type Checking

TypeScript provides strong typing, which helps catch potential errors early in development. Always enable strict mode in your tsconfig.json:

{
  "compilerOptions": {
    "strict": true
  }
}

Benefits:

  • Prevents implicit any types.
  • Ensures better type inference.
  • Helps in detecting runtime errors during development.

2. Avoid Using any

Using any defeats the purpose of TypeScript's type system. Instead, define explicit types:

// Bad Practice
let userData: any = fetchUserData();

// Good Practice
interface User {
  id: number;
  name: string;
}
let userData: User = fetchUserData();

3. Validate User Input

Never trust user input. Use validation libraries like zod or class-validator to ensure data integrity.

import { z } from 'zod';

const userSchema = z.object({
  name: z.string().min(2),
  age: z.number().positive()
});

const userData = getUserInput();
const validatedData = userSchema.parse(userData);

4. Use Environment Variables for Sensitive Data

Never hardcode secrets in your source code. Use .env files and process.env:

import dotenv from 'dotenv';
dotenv.config();

const dbPassword = process.env.DB_PASSWORD;

5. Implement Proper Error Handling

Avoid exposing sensitive information in error messages. Use a global error handler:

class AppError extends Error {
  constructor(message: string, public statusCode: number) {
    super(message);
  }
}

function errorHandler(err: AppError) {
  console.error(err.message);
}

6. Secure API Requests

When making API requests, use HTTPS and authenticate properly:

const fetchSecureData = async () => {
  const response = await fetch('https://api.example.com/data', {
    method: 'GET',
    headers: {
      Authorization: `Bearer undefined`
    }
  });
  return response.json();
};

7. Prevent Cross-Site Scripting (XSS)

Sanitize user-generated content before rendering:

import DOMPurify from 'dompurify';

const safeHTML = DOMPurify.sanitize(userInput);

8. Avoid SQL Injection

Use parameterized queries when interacting with databases:

import { Pool } from 'pg';

const pool = new Pool();
const userId = 1;
const result = await pool.query('SELECT * FROM users WHERE id = $1', [userId]);

9. Keep Dependencies Updated

Regularly update dependencies to avoid security vulnerabilities:

npm audit fix --force

10. Implement Role-Based Access Control (RBAC)

Restrict user permissions based on roles:

const userRoles = {
  admin: ['read', 'write', 'delete'],
  user: ['read']
};

const hasPermission = (role: string, action: string) => userRoles[role]?.includes(action);

Conclusion

By following these secure coding practices, you can enhance the security of your TypeScript applications, mitigate risks, and ensure robust software development.