TypeScript is a strongly typed programming language that builds on JavaScript by adding optional static type checking. Developed by Microsoft, it compiles to plain JavaScript that runs anywhere JavaScript executes (browsers, Node.js, Deno, Bun). TypeScript is a superset of JavaScript - all valid JavaScript code is valid TypeScript. Version 5.7 (November 2024) brings compile caching (2-3x faster builds), never-initialized variable checks, and ES2024 support. TypeScript provides compile-time error detection, enhanced IDE autocomplete/refactoring, and type annotations as built-in documentation. With 60M+ weekly npm downloads and 85%+ enterprise Node.js adoption (2025), TypeScript is the standard for type-safe JavaScript development. Use with 'strict: true' in tsconfig.json for maximum type safety.
TypeScript FAQ & Answers
92 expert TypeScript answers researched from official documentation. Every answer cites authoritative sources you can verify.
Jump to section:
unknown
90 questionsTypeScript is a superset of JavaScript - every JavaScript program is also a valid TypeScript program. The relationship: TypeScript adds static typing to JavaScript while maintaining full compatibility. TypeScript code compiles to JavaScript, targeting any ECMAScript version (ES3, ES5, ES2015+, etc.). Key advantages: catch errors at compile time instead of runtime, better tooling support, improved code maintainability. TypeScript transpiles to clean, readable JavaScript that can run in any environment. The ecosystem includes DefinitelyTyped for JavaScript library typings, seamless integration with existing JavaScript projects, and gradual adoption capability.
Interfaces and types both define object shapes but have key differences. Interfaces: support declaration merging (multiple declarations combine), can be extended and implemented, better for object-oriented patterns, limited to describing object shapes. Types: more flexible with unions/intersections, support mapped types and conditional types, can alias primitives, cannot be reopened after declaration. Modern convention: prefer interface for objects that might be extended or for public APIs, use type for unions, intersections, utility types, and primitive aliases. Performance difference is negligible in modern TypeScript.
Generics enable creating reusable components that work with multiple types while preserving type safety. They use type parameters (commonly
Use generics when creating components that need to work with multiple data types while maintaining type safety. Ideal scenarios: utility functions (map, filter, reduce), data structures (Stack
'any' is a special type that disables type checking for a value, allowing any operation without compile-time errors. Variables typed as 'any' accept any value, and you can perform any operation on them (access properties, call methods, assign to other types). 'any' completely opts out of type checking, effectively returning to JavaScript behavior. Common use cases: migrating existing JavaScript codebases, working with third-party libraries without type definitions, handling truly dynamic data (JSON from unknown sources), and quick prototyping. However, 'any' eliminates TypeScript's benefits and should be minimized in production code.
Avoid 'any' because it defeats TypeScript's primary purpose: type safety. Problems with 'any': no compile-time error checking, no IDE autocomplete or refactoring support, errors discovered at runtime instead of development, type information spreads to other code (type pollution), and makes code harder to understand and maintain. Better alternatives: 'unknown' for type-safe dynamic data, generics for reusable type-safe components, union types for multiple known types, and specific interfaces. Enable 'noImplicitAny' in tsconfig.json to catch implicit any usage. Rule of thumb: only use 'any' as a last resort when migrating legacy code or working with untyped third-party libraries, and replace with proper types as soon as possible.
'any' opts out completely of type checking - any operation allowed without errors. 'unknown' is the type-safe alternative - forces proper type checking before operations. With 'unknown': cannot access properties, call methods, or use in operations without first narrowing the type. Example: function process(value: unknown) { if (typeof value === 'string') { console.log(value.toUpperCase()); // OK } } vs 'any': no checks needed but unsafe. 'unknown' requires explicit type guards: typeof, instanceof, custom type predicates. Use 'unknown' for: user input, JSON responses, API data, migration from JavaScript. 'any' is an escape hatch; 'unknown' enforces safe handling. Modern TypeScript best practices favor 'unknown' over 'any'.
Union types allow values to be one of several specified types using the pipe operator (|). Syntax: string | number | boolean. Union types represent values that can be any of the included types. Example: let value: string | number = 'hello'; value = 42; // both valid. Union types enable flexible APIs while maintaining type safety. Common patterns: function parameters accepting multiple types, API responses with varying structures, configuration options with different value types. TypeScript requires type narrowing before accessing type-specific properties. Union types are the foundation for many TypeScript patterns including discriminated unions, optional chaining, and null safety.
Use union types by first declaring them with pipe syntax, then narrowing types before type-specific operations. Type narrowing methods: typeof for primitives, instanceof for classes, in operator for properties, discriminant properties for object types. Example: function process(value: string | number) { if (typeof value === 'string') { return value.toUpperCase(); // TypeScript knows it's string } return value.toFixed(2); // TypeScript knows it's number }. Discriminated unions: type Result = { success: true, data: any } | { success: false, error: string }; function handle(result: Result) { if (result.success) { console.log(result.data); } else { console.log(result.error); } }. Common use cases: API responses, component props, configuration objects. Always consider using unknown for truly dynamic data instead of overly broad unions.
Intersection types combine multiple types into one using the ampersand operator (&), creating a type that has all properties from all constituent types. Syntax: Type1 & Type2 & Type3. The resulting type has the union of all properties and methods. Example: type Person = { name: string; }; type Employee = { id: number; }; type PersonEmployee = Person & Employee; // { name: string; id: number; }. Intersections work with interfaces, types, and can create complex object types by composing simpler ones. Properties with the same name must have compatible types. Intersection types are fundamental for composition patterns, mixins, and creating rich types from multiple sources. Unlike union types (OR logic), intersections use AND logic - the type must satisfy all constituent types simultaneously.
Use intersection types when you need to combine multiple type definitions into one comprehensive type. Ideal scenarios: mixins (combining multiple behaviors), plugin architectures (objects must satisfy multiple interfaces), API response types extending base types, configuration objects from multiple sources, and composition over inheritance patterns. Example: type AdminUser = User & { permissions: string[]; } & { loginHistory: Date[]; }. Use for creating specialized types from general ones: type DatabaseConfig = BaseConfig & { connectionUrl: string; timeout: number; }. Common in React: type Props = ComponentProps & { additionalProp: string; }. Avoid intersections with primitive types (string & number = never). Prefer intersections over deep inheritance hierarchies. Essential for flexible, composable type systems and implementing the composition pattern effectively.
Type guards are expressions that perform runtime checks to narrow down types within conditional blocks. They tell TypeScript the specific type of a value, enabling type-safe operations. TypeScript recognizes several built-in type guard patterns: typeof checks for primitive types (string, number, boolean), instanceof checks for class instances, in operator for property existence, and truthiness/falsy checks. Type guards change the type within their scope: function process(value: unknown) { if (typeof value === 'string') { // TypeScript knows value is string here } }. Custom type guards use type predicates: function isString(val: unknown): val is string { return typeof val === 'string'; }. Type guards are essential for working with union types, unknown values, and maintaining type safety in dynamic scenarios.
Implement type guards using various patterns. Built-in type guards: typeof val === 'string' (primitives), val instanceof ClassName (classes), 'prop' in obj (property existence), !!val (truthiness). Custom type guards use type predicates: function isString(val: unknown): val is string { return typeof val === 'string'; } function isArray
Partial
Required
Readonly
Pick<T, K> creates a new type by selecting specific properties K from type T. K must be a union of property names from T. Syntax: type UserPreview = Pick<User, 'id' | 'name'>;. If User = { id: number; name: string; email: string; password: string; }, then UserPreview = { id: number; name: string; }. Pick extracts a subset of properties while maintaining their original types and modifiers (readonly, optional). Use cases: creating API response DTOs that exclude sensitive data, component prop selection, function return types with specific fields, and data transfer between system boundaries. Common pattern: function createUserResponse(user: User): Pick<User, 'id' | 'name' | 'email'> { return { id: user.id, name: user.name, email: user.email }; }. Essential for creating specialized interfaces from general types and maintaining type safety in data transformations.
Omit<T, K> creates a new type by excluding specific properties K from type T. It's the opposite of Pick. Syntax: type UserWithoutPassword = Omit<User, 'password'>;. If User = { id: number; name: string; email: string; password: string; }, then UserWithoutPassword = { id: number; name: string; email: string; }. Omit constructs a type with all properties from T except those specified in K. Use cases: removing sensitive fields for external APIs, excluding internal properties from public interfaces, creating clean data transfer objects, and implementing inheritance-like behavior without classes. Implementation: Omit<T, K> = Pick<T, Exclude<keyof T, K>>. Common pattern: function createUserPublicProfile(user: User): Omit<User, 'password' | 'internalId'> { const { password, internalId, ...publicData } = user; return publicData; }. Essential for data sanitization and API security.
Record<K, V> creates an object type where all keys are of type K and all values are of type V. K must be a string, number, or symbol type (or union of these). Syntax: type StringMap = Record<string, string>; type UserRoles = Record<string, string[]>;. Example: const roles: Record<string, string[]> = { admin: ['read', 'write', 'delete'], user: ['read'] };. Record is essentially a typed object for dictionary-like structures. Use cases: lookup tables, dictionaries, configuration objects, mapping data, and key-value collections. More type-safe than general object types because keys and values are constrained. Alternative to Map when object syntax is preferred. Common pattern: type ApiResponse = Record<string, any>; for dynamic JSON responses. Essential for creating type-safe object collections and maintaining consistency in key-value data structures.
Exclude<T, U> creates a union type by removing from T all types that are assignable to U. Syntax: type StringOnly = Exclude<string | number | boolean, number | boolean>; // results in string. It filters out unwanted types from unions. Implementation works by checking if each type in T is assignable to U and removing those that are. Use cases: filtering union types to specific subtypes, removing null/undefined from unions (though NonNullable is better for this), creating clean type unions, and type-level programming. Common pattern: type NonEmptyString = Exclude<string | undefined, undefined>; // same as NonNullable
Extract<T, U> creates a union type by extracting from T all types that are assignable to U. It's the opposite of Exclude. Syntax: type StringOrNumber = Extract<string | number | boolean, string | number>; // results in string | number. Extract filters unions to include only types matching the condition. Use cases: selecting specific subtypes from larger unions, type filtering in generic code, creating constrained type sets, and conditional type selection. Common pattern: type SerializableProperties
NonNullable
'strict': true in tsconfig.json enables all strict type checking options simultaneously, representing TypeScript's most rigorous type checking mode. It's a shorthand flag that activates multiple individual strict options together. Strict mode includes: noImplicitAny (error on implicit any types), strictNullChecks (null and undefined not assignable to non-nullable types), strictFunctionTypes (enforce contravariant parameter checking for functions), strictBindCallApply (proper type checking for bind, call, apply methods), strictPropertyInitialization (class properties must be initialized in constructor), noImplicitThis (error on 'this' with implicit any type), and alwaysStrict (always output JavaScript in strict mode). Recommended for all new TypeScript projects to maximize type safety and catch errors early.
Strict mode enables comprehensive type checking across seven key areas: (1) noImplicitAny - prevents variables from implicitly having 'any' type, (2) strictNullChecks - treats null and undefined as distinct types, preventing null assignment to non-nullable types, (3) strictFunctionTypes - disables bivariant parameter checking for functions, enforcing proper function type compatibility, (4) strictBindCallApply - ensures bind, call, and apply methods work correctly with typed arguments, (5) strictPropertyInitialization - requires class properties to be initialized in constructors unless declared with definite assignment assertion, (6) noImplicitThis - catches 'this' context issues, (7) alwaysStrict - outputs JavaScript in strict mode. Together, these options catch common runtime errors at compile time, enforce better coding practices, and significantly improve type safety. Start with strict mode for new projects, or enable individual flags gradually when migrating existing codebases.
Decorators are special declarations that can be attached to classes, methods, accessors, properties, or parameters using the @expression syntax. They are functions that modify the behavior of the decorated element at design time. Decorators are experimental in TypeScript and require 'experimentalDecorators': true in tsconfig.json. Types include: class decorators (modify constructor), method decorators (modify method behavior), accessor decorators (modify getters/setters), property decorators (observe property definition), and parameter decorators (observe parameter declaration). Decorators enable metaprogramming patterns and are used extensively in frameworks like Angular (Component, Injectable), NestJS (Controller, Get), and TypeORM (Entity, Column). Note: Decorators are a Stage 3 TC39 proposal but TypeScript's implementation may differ from the final JavaScript standard.
Decorators excel in several key scenarios: (1) Dependency Injection - Angular (@Injectable), NestJS (@Injectable) automatically manage class instantiation and dependencies, (2) Validation - class-validator decorators (@IsString, @MinLength) automatically validate object properties, (3) Logging/Authentication - custom decorators add cross-cutting concerns like logging or auth checks to methods, (4) ORM/Database mapping - TypeORM (@Entity, @Column) define database schemas, (5) Routing - Express/NestJS decorators (@Get, @Post) define API endpoints, (6) Caching - @Cache() decorators cache method results, (7) Documentation - Swagger decorators (@ApiProperty) generate API docs. Decorators separate concerns cleanly, eliminate boilerplate code, and enable declarative programming. Essential in framework development, but use judiciously as they can make code flow harder to follow. Consider composition patterns for simpler use cases.
Type inference is TypeScript's automatic process of determining types when they're not explicitly specified by the developer. TypeScript analyzes the code context to assign the most specific type possible. Inference occurs in several scenarios: variable initialization (let x = 3; // infers number), function return types (function greet() { return 'hello'; } // infers string return type), contextual typing (array.map(x => x.length) // infers parameter types), and best common type analysis (let mixed = [1, 'hello', true]; // infers string | number | boolean). TypeScript's inference engine considers assignments, return statements, expressions, and control flow to determine appropriate types. Inference maintains type safety while reducing the need for explicit type annotations, making code more concise while preserving all type checking benefits.
TypeScript inference follows several algorithms: (1) Best Common Type - selects the most specific type that encompasses all values in an array or object literal, (2) Contextual Typing - infers types based on where a value is used (function parameters, array methods), (3) Control Flow Analysis - refines types based on code paths and conditions, (4) Generic Type Inference - deduces generic types from function calls. Example: function identity
Mapped types transform existing types by creating new types that iterate over the keys of an original type. They use the syntax { [K in Keys]: Type } where Keys is typically keyof T for iterating over all properties of type T. Mapped types can modify property modifiers (add/remove readonly, optional) and transform property types. Examples: type Readonly
Regular const declarations create runtime immutability (variables cannot be reassigned), while 'as const' assertions create compile-time immutability (types become literal and readonly). const let x = 3; // x can be reassigned to any number; 'as const': const obj = { x: 10 } as const; // obj.x is exactly 10, not number. Key differences: const affects runtime behavior and prevents reassignment, 'as const' affects the type system only and prevents type widening. 'as const' effects: literals don't widen (3 stays 3, not number), arrays become readonly tuples, object properties become readonly, nested objects get deep readonly treatment. Example: const colors = ['red', 'green', 'blue'] as const; // type: readonly ['red', 'green', 'blue']. Use 'as const' for enum-like behavior, exact literal types, and when TypeScript's inference is too general. Use regular const for variable immutability where flexible types are desired.
Conditional types select types based on conditions using syntax similar to ternary operators: T extends U ? X : Y. They enable type-level programming and sophisticated type transformations. The extends keyword checks if type T is assignable to type U, returning type X if true, Y if false. Conditional types work with generics to create flexible type relationships. The 'infer' keyword extracts type information from within conditional types. Built-in examples include NonNullable
'strictNullChecks': true in tsconfig.json makes null and undefined distinct types that cannot be assigned to non-nullable types without explicit checking. Without this option, null and undefined are assignable to any type, leading to potential runtime null reference errors. With strictNullChecks enabled, you must explicitly handle null/undefined possibilities through union types (string | null), optional chaining (?.), nullish coalescing (??), or type guards. This catches the most common JavaScript runtime error ('Cannot read property of undefined') at compile time. It enforces null safety patterns and eliminates entire classes of bugs. Essential for production code quality, especially in applications dealing with API responses, user input, or external data. Modern TypeScript development standard that significantly improves code reliability.
Template literal types build string types by combining string literals, variables, and operations using template literal syntax (backticks with ${} placeholders). They enable type-safe string manipulation at compile time. Syntax: type EventNameon${Capitalize<T>}Changed; Example: type ClickEvent = EventName<'click'>; // 'onClickChanged'. Combine with unions for comprehensive string patterns: type HttpMethod = 'GET' | 'POST'; type ApiEndpoint/${T}; type UserApi = ApiEndpoint<'users'>; // '/users'. Built-in string manipulation types: Uppercase
Declaration files (.d.ts) provide type information for JavaScript code without containing implementation details. They describe the 'shape' of JavaScript libraries, enabling TypeScript to understand and type-check code that uses those libraries. Declaration files contain only type declarations using the 'declare' keyword and cannot include executable code. They enable TypeScript/JavaScript interoperability, providing autocomplete, error checking, and documentation for JavaScript libraries. The DefinitelyTyped repository (@types packages on npm) provides declaration files for most popular JavaScript libraries. Declaration files can be automatically generated from TypeScript source code or written manually for existing JavaScript. Essential for using JavaScript libraries in TypeScript projects, sharing type definitions across projects, and gradually migrating JavaScript codebases to TypeScript.
Index signatures define types for objects with dynamic property names, allowing access to properties that aren't known at compile time. Syntax: { [key: string]: Type } or { [index: number]: Type }. Key types must be string, number, or symbol. Example: interface StringMap { [key: string]: string; } const map: StringMap = { name: 'John', age: '30' };. All properties must conform to the signature type, including known properties. Use cases: dictionaries, JSON objects, configuration objects, API response data, and any object with dynamic keys. Alternative: Record<string, Type> utility type. Prefer specific properties with optional modifiers when property names are known. Consider Map for true dictionary functionality with better performance and methods. Index signatures are essential when working with data from external sources where property names aren't predetermined.
'never' represents values that never occur, functioning as TypeScript's bottom type. It's used to indicate impossible states or unreachable code paths. Properties: 'never' is assignable to every type, but no type (except 'never' itself) is assignable to 'never'. Use cases: (1) Functions that never return (always throw or have infinite loops): function throwError(message: string): never { throw new Error(message); }, (2) Exhaustive checking in switch statements with discriminated unions: default case with 'never' ensures all cases are handled, (3) Impossible types: string & number = never, (4) Type guards that always return false: function isNever(val: never): val is never { return false; }. The 'never' type helps ensure code correctness by making impossible states unrepresentable. Essential for type-safe state machines, exhaustive pattern matching, and maintaining type safety in complex conditional logic.
Abstract classes and interfaces serve different purposes: Abstract classes: cannot be instantiated directly, can contain implementation, support access modifiers (private, protected, public), can have constructors, enable single inheritance, are runtime entities, and provide shared code implementation. Interfaces: pure contracts with no implementation, all members are implicitly public, support multiple inheritance (extends multiple interfaces), are compile-time only (erased in generated JavaScript), cannot have constructors, and define only shape/structure. Use abstract classes when: sharing implementation code, needing protected members, requiring constructor logic, or building inheritance hierarchies. Use interfaces when: defining pure contracts, needing multiple inheritance, working with structural typing, or creating public APIs. Abstract classes are is-a relationships; interfaces are can-do relationships. Choose based on whether you need shared implementation (abstract class) or just shape definition (interface).
Discriminated unions combine literal types with unions to enable type-safe handling of different object shapes. Requirements: (1) A common property in all union members (the discriminant/tag), (2) The discriminant must be a literal type (string, number, boolean literal), (3) A union of the different object types. TypeScript narrows the type based on the discriminant value. Example: type Result = { success: true, data: T } | { success: false, error: string }; function handleResult(result: Result) { if (result.success) { console.log(result.data); // TypeScript knows this exists } else { console.log(result.error); // TypeScript knows this exists } }. The discriminant acts as a type tag that uniquely identifies each variant. Use cases: API responses (success/error states), Redux actions, state machines, configuration objects, and any scenario with mutually exclusive object shapes. Eliminates runtime type checking and enables exhaustive checking with 'never' type. Essential for type-safe state management.
'keyof' creates a union type of an object type's property names. Syntax: keyof Type. Example: interface User { id: number; name: string; } type UserKeys = keyof User; // 'id' | 'name'. 'keyof' works with classes, interfaces, and type aliases. It's commonly combined with indexed access types (T[K]) for type-safe property access: function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; }. 'keyof' respects property modifiers (readonly, optional) and works with mapped types. Use cases: generic property access, building type-safe object utilities, constraint validation, dynamic property manipulation, and creating flexible APIs that work with object properties. 'keyof' maintains type safety when working with dynamic property names and enables powerful type-level programming patterns. Essential for building reusable, type-safe utilities that operate on object properties.
Namespaces are TypeScript's way to group related code and prevent global scope pollution using the namespace Name { } syntax. Formerly called 'internal modules' in early TypeScript. Namespaces create nested object structures at runtime that can contain classes, interfaces, functions, and variables. They support multi-file organization using reference comments (/// <reference path='otherFile.ts' />) and can be split across multiple files. Namespaces are compiled into JavaScript objects that maintain the nested structure. Example: namespace MathUtils { export function add(a: number, b: number): number { return a + b; } } compiles to var MathUtils; (function(MathUtils) { MathUtils.add = function(a, b) { return a + b; }; })(MathUtils || (MathUtils = {}));. Namespaces are mainly used today for declaration files and legacy code organization.
Modules are TypeScript's implementation of ES6 modules, providing file-based code organization using import and export statements. Each file with imports/exports is automatically a module with its own scope. Modules support named exports (export const PI = 3.14;), default exports (export default class Calculator {}), and re-exports (export * from './utils';). Modules can import specific items (import { add } from './math';) or entire modules (import * as Math from './math';). They enable tree-shaking (elimination of unused code), better bundling with webpack/Rollup, and align with modern JavaScript standards. Example: // math.ts export function add(a, b) { return a + b; } // main.ts import { add } from './math'; console.log(add(2, 3));. Modules are the recommended approach for organizing TypeScript code.
Key differences: Scope - Namespaces: global scope management with nested objects; Modules: file-based scope with proper isolation. Runtime - Namespaces: emit nested JavaScript objects; Modules: emit separate files with import/export. Tooling - Namespaces: limited tree-shaking, older bundler support; Modules: excellent tree-shaking, modern bundler optimization. Usage - Namespaces: legacy TypeScript, declaration files, global utilities; Modules: preferred for all new code, standard JavaScript approach. Dependencies - Namespaces: use reference comments for multi-file; Modules: use import/export with module resolution. Performance - Modules generally better due to smaller bundles and better code splitting. Modern TypeScript strongly favors modules over namespaces for better maintainability, tooling support, and alignment with JavaScript ecosystem standards.
Assertion functions validate conditions and narrow types within their scope, throwing errors if conditions aren't met. Two forms: (1) asserts condition: function assert(condition: unknown, message?: string): asserts condition; (2) asserts parameter is Type: function isString(value: unknown): asserts value is string. When an assertion function executes successfully, TypeScript assumes the asserted condition is true for all subsequent code. Example: function assertString(val: unknown): asserts val is string { if (typeof val !== 'string') throw new Error('Not a string'); } function process(val: unknown) { assertString(val); console.log(val.toUpperCase()); // TypeScript knows val is string }. Built-in: console.assert in Node.js. Use cases: input validation, preconditions, invariants, runtime type checking. Assertion functions provide cleaner syntax than type guards when you want to throw on invalid input rather than return false. TypeScript 3.7+ feature essential for runtime type validation.
Essential tsconfig.json compiler options: target - specifies ECMAScript version (ES2020, ESNext), module - module system (ESNext, CommonJS, AMD), strict - enables all strict type checking options, outDir - output directory for compiled files, rootDir - root directory of input files, include/exclude - file selection patterns, lib - built-in type definitions (ES2020, DOM), moduleResolution - strategy for resolving modules (node, bundler), esModuleInterop - enables CommonJS/ES Module interop, skipLibCheck - skip type checking of declaration files, resolveJsonModule - allows importing JSON files, declaration - generate .d.ts files, sourceMap - generate source maps for debugging, allowSyntheticDefaultImports - consistent default import behavior. Start with 'tsc --init' for defaults. Use 'strict: true' for new projects. Adjust target/module based on runtime environment. Project references for monorepos with multiple related projects.
TypeScript is a strongly typed programming language that builds on JavaScript by adding optional static type checking. Developed by Microsoft, it compiles to plain JavaScript that runs anywhere JavaScript executes (browsers, Node.js, Deno, Bun). TypeScript is a superset of JavaScript - all valid JavaScript code is valid TypeScript. Version 5.7 (November 2024) brings compile caching (2-3x faster builds), never-initialized variable checks, and ES2024 support. TypeScript provides compile-time error detection, enhanced IDE autocomplete/refactoring, and type annotations as built-in documentation. With 60M+ weekly npm downloads and 85%+ enterprise Node.js adoption (2025), TypeScript is the standard for type-safe JavaScript development. Use with 'strict: true' in tsconfig.json for maximum type safety.
TypeScript is a superset of JavaScript - every JavaScript program is also a valid TypeScript program. The relationship: TypeScript adds static typing to JavaScript while maintaining full compatibility. TypeScript code compiles to JavaScript, targeting any ECMAScript version (ES3, ES5, ES2015+, etc.). Key advantages: catch errors at compile time instead of runtime, better tooling support, improved code maintainability. TypeScript transpiles to clean, readable JavaScript that can run in any environment. The ecosystem includes DefinitelyTyped for JavaScript library typings, seamless integration with existing JavaScript projects, and gradual adoption capability.
Interfaces and types both define object shapes but have key differences. Interfaces: support declaration merging (multiple declarations combine), can be extended and implemented, better for object-oriented patterns, limited to describing object shapes. Types: more flexible with unions/intersections, support mapped types and conditional types, can alias primitives, cannot be reopened after declaration. Modern convention: prefer interface for objects that might be extended or for public APIs, use type for unions, intersections, utility types, and primitive aliases. Performance difference is negligible in modern TypeScript.
Generics enable creating reusable components that work with multiple types while preserving type safety. They use type parameters (commonly
Use generics when creating components that need to work with multiple data types while maintaining type safety. Ideal scenarios: utility functions (map, filter, reduce), data structures (Stack
'any' is a special type that disables type checking for a value, allowing any operation without compile-time errors. Variables typed as 'any' accept any value, and you can perform any operation on them (access properties, call methods, assign to other types). 'any' completely opts out of type checking, effectively returning to JavaScript behavior. Common use cases: migrating existing JavaScript codebases, working with third-party libraries without type definitions, handling truly dynamic data (JSON from unknown sources), and quick prototyping. However, 'any' eliminates TypeScript's benefits and should be minimized in production code.
Avoid 'any' because it defeats TypeScript's primary purpose: type safety. Problems with 'any': no compile-time error checking, no IDE autocomplete or refactoring support, errors discovered at runtime instead of development, type information spreads to other code (type pollution), and makes code harder to understand and maintain. Better alternatives: 'unknown' for type-safe dynamic data, generics for reusable type-safe components, union types for multiple known types, and specific interfaces. Enable 'noImplicitAny' in tsconfig.json to catch implicit any usage. Rule of thumb: only use 'any' as a last resort when migrating legacy code or working with untyped third-party libraries, and replace with proper types as soon as possible.
'any' opts out completely of type checking - any operation allowed without errors. 'unknown' is the type-safe alternative - forces proper type checking before operations. With 'unknown': cannot access properties, call methods, or use in operations without first narrowing the type. Example: function process(value: unknown) { if (typeof value === 'string') { console.log(value.toUpperCase()); // OK } } vs 'any': no checks needed but unsafe. 'unknown' requires explicit type guards: typeof, instanceof, custom type predicates. Use 'unknown' for: user input, JSON responses, API data, migration from JavaScript. 'any' is an escape hatch; 'unknown' enforces safe handling. Modern TypeScript best practices favor 'unknown' over 'any'.
Union types allow values to be one of several specified types using the pipe operator (|). Syntax: string | number | boolean. Union types represent values that can be any of the included types. Example: let value: string | number = 'hello'; value = 42; // both valid. Union types enable flexible APIs while maintaining type safety. Common patterns: function parameters accepting multiple types, API responses with varying structures, configuration options with different value types. TypeScript requires type narrowing before accessing type-specific properties. Union types are the foundation for many TypeScript patterns including discriminated unions, optional chaining, and null safety.
Use union types by first declaring them with pipe syntax, then narrowing types before type-specific operations. Type narrowing methods: typeof for primitives, instanceof for classes, in operator for properties, discriminant properties for object types. Example: function process(value: string | number) { if (typeof value === 'string') { return value.toUpperCase(); // TypeScript knows it's string } return value.toFixed(2); // TypeScript knows it's number }. Discriminated unions: type Result = { success: true, data: any } | { success: false, error: string }; function handle(result: Result) { if (result.success) { console.log(result.data); } else { console.log(result.error); } }. Common use cases: API responses, component props, configuration objects. Always consider using unknown for truly dynamic data instead of overly broad unions.
Intersection types combine multiple types into one using the ampersand operator (&), creating a type that has all properties from all constituent types. Syntax: Type1 & Type2 & Type3. The resulting type has the union of all properties and methods. Example: type Person = { name: string; }; type Employee = { id: number; }; type PersonEmployee = Person & Employee; // { name: string; id: number; }. Intersections work with interfaces, types, and can create complex object types by composing simpler ones. Properties with the same name must have compatible types. Intersection types are fundamental for composition patterns, mixins, and creating rich types from multiple sources. Unlike union types (OR logic), intersections use AND logic - the type must satisfy all constituent types simultaneously.
Use intersection types when you need to combine multiple type definitions into one comprehensive type. Ideal scenarios: mixins (combining multiple behaviors), plugin architectures (objects must satisfy multiple interfaces), API response types extending base types, configuration objects from multiple sources, and composition over inheritance patterns. Example: type AdminUser = User & { permissions: string[]; } & { loginHistory: Date[]; }. Use for creating specialized types from general ones: type DatabaseConfig = BaseConfig & { connectionUrl: string; timeout: number; }. Common in React: type Props = ComponentProps & { additionalProp: string; }. Avoid intersections with primitive types (string & number = never). Prefer intersections over deep inheritance hierarchies. Essential for flexible, composable type systems and implementing the composition pattern effectively.
Type guards are expressions that perform runtime checks to narrow down types within conditional blocks. They tell TypeScript the specific type of a value, enabling type-safe operations. TypeScript recognizes several built-in type guard patterns: typeof checks for primitive types (string, number, boolean), instanceof checks for class instances, in operator for property existence, and truthiness/falsy checks. Type guards change the type within their scope: function process(value: unknown) { if (typeof value === 'string') { // TypeScript knows value is string here } }. Custom type guards use type predicates: function isString(val: unknown): val is string { return typeof val === 'string'; }. Type guards are essential for working with union types, unknown values, and maintaining type safety in dynamic scenarios.
Implement type guards using various patterns. Built-in type guards: typeof val === 'string' (primitives), val instanceof ClassName (classes), 'prop' in obj (property existence), !!val (truthiness). Custom type guards use type predicates: function isString(val: unknown): val is string { return typeof val === 'string'; } function isArray
Partial
Required
Readonly
Pick<T, K> creates a new type by selecting specific properties K from type T. K must be a union of property names from T. Syntax: type UserPreview = Pick<User, 'id' | 'name'>;. If User = { id: number; name: string; email: string; password: string; }, then UserPreview = { id: number; name: string; }. Pick extracts a subset of properties while maintaining their original types and modifiers (readonly, optional). Use cases: creating API response DTOs that exclude sensitive data, component prop selection, function return types with specific fields, and data transfer between system boundaries. Common pattern: function createUserResponse(user: User): Pick<User, 'id' | 'name' | 'email'> { return { id: user.id, name: user.name, email: user.email }; }. Essential for creating specialized interfaces from general types and maintaining type safety in data transformations.
Omit<T, K> creates a new type by excluding specific properties K from type T. It's the opposite of Pick. Syntax: type UserWithoutPassword = Omit<User, 'password'>;. If User = { id: number; name: string; email: string; password: string; }, then UserWithoutPassword = { id: number; name: string; email: string; }. Omit constructs a type with all properties from T except those specified in K. Use cases: removing sensitive fields for external APIs, excluding internal properties from public interfaces, creating clean data transfer objects, and implementing inheritance-like behavior without classes. Implementation: Omit<T, K> = Pick<T, Exclude<keyof T, K>>. Common pattern: function createUserPublicProfile(user: User): Omit<User, 'password' | 'internalId'> { const { password, internalId, ...publicData } = user; return publicData; }. Essential for data sanitization and API security.
Record<K, V> creates an object type where all keys are of type K and all values are of type V. K must be a string, number, or symbol type (or union of these). Syntax: type StringMap = Record<string, string>; type UserRoles = Record<string, string[]>;. Example: const roles: Record<string, string[]> = { admin: ['read', 'write', 'delete'], user: ['read'] };. Record is essentially a typed object for dictionary-like structures. Use cases: lookup tables, dictionaries, configuration objects, mapping data, and key-value collections. More type-safe than general object types because keys and values are constrained. Alternative to Map when object syntax is preferred. Common pattern: type ApiResponse = Record<string, any>; for dynamic JSON responses. Essential for creating type-safe object collections and maintaining consistency in key-value data structures.
Exclude<T, U> creates a union type by removing from T all types that are assignable to U. Syntax: type StringOnly = Exclude<string | number | boolean, number | boolean>; // results in string. It filters out unwanted types from unions. Implementation works by checking if each type in T is assignable to U and removing those that are. Use cases: filtering union types to specific subtypes, removing null/undefined from unions (though NonNullable is better for this), creating clean type unions, and type-level programming. Common pattern: type NonEmptyString = Exclude<string | undefined, undefined>; // same as NonNullable
Extract<T, U> creates a union type by extracting from T all types that are assignable to U. It's the opposite of Exclude. Syntax: type StringOrNumber = Extract<string | number | boolean, string | number>; // results in string | number. Extract filters unions to include only types matching the condition. Use cases: selecting specific subtypes from larger unions, type filtering in generic code, creating constrained type sets, and conditional type selection. Common pattern: type SerializableProperties
NonNullable
'strict': true in tsconfig.json enables all strict type checking options simultaneously, representing TypeScript's most rigorous type checking mode. It's a shorthand flag that activates multiple individual strict options together. Strict mode includes: noImplicitAny (error on implicit any types), strictNullChecks (null and undefined not assignable to non-nullable types), strictFunctionTypes (enforce contravariant parameter checking for functions), strictBindCallApply (proper type checking for bind, call, apply methods), strictPropertyInitialization (class properties must be initialized in constructor), noImplicitThis (error on 'this' with implicit any type), and alwaysStrict (always output JavaScript in strict mode). Recommended for all new TypeScript projects to maximize type safety and catch errors early.
Strict mode enables comprehensive type checking across seven key areas: (1) noImplicitAny - prevents variables from implicitly having 'any' type, (2) strictNullChecks - treats null and undefined as distinct types, preventing null assignment to non-nullable types, (3) strictFunctionTypes - disables bivariant parameter checking for functions, enforcing proper function type compatibility, (4) strictBindCallApply - ensures bind, call, and apply methods work correctly with typed arguments, (5) strictPropertyInitialization - requires class properties to be initialized in constructors unless declared with definite assignment assertion, (6) noImplicitThis - catches 'this' context issues, (7) alwaysStrict - outputs JavaScript in strict mode. Together, these options catch common runtime errors at compile time, enforce better coding practices, and significantly improve type safety. Start with strict mode for new projects, or enable individual flags gradually when migrating existing codebases.
Decorators are special declarations that can be attached to classes, methods, accessors, properties, or parameters using the @expression syntax. They are functions that modify the behavior of the decorated element at design time. Decorators are experimental in TypeScript and require 'experimentalDecorators': true in tsconfig.json. Types include: class decorators (modify constructor), method decorators (modify method behavior), accessor decorators (modify getters/setters), property decorators (observe property definition), and parameter decorators (observe parameter declaration). Decorators enable metaprogramming patterns and are used extensively in frameworks like Angular (Component, Injectable), NestJS (Controller, Get), and TypeORM (Entity, Column). Note: Decorators are a Stage 3 TC39 proposal but TypeScript's implementation may differ from the final JavaScript standard.
Decorators excel in several key scenarios: (1) Dependency Injection - Angular (@Injectable), NestJS (@Injectable) automatically manage class instantiation and dependencies, (2) Validation - class-validator decorators (@IsString, @MinLength) automatically validate object properties, (3) Logging/Authentication - custom decorators add cross-cutting concerns like logging or auth checks to methods, (4) ORM/Database mapping - TypeORM (@Entity, @Column) define database schemas, (5) Routing - Express/NestJS decorators (@Get, @Post) define API endpoints, (6) Caching - @Cache() decorators cache method results, (7) Documentation - Swagger decorators (@ApiProperty) generate API docs. Decorators separate concerns cleanly, eliminate boilerplate code, and enable declarative programming. Essential in framework development, but use judiciously as they can make code flow harder to follow. Consider composition patterns for simpler use cases.
Type inference is TypeScript's automatic process of determining types when they're not explicitly specified by the developer. TypeScript analyzes the code context to assign the most specific type possible. Inference occurs in several scenarios: variable initialization (let x = 3; // infers number), function return types (function greet() { return 'hello'; } // infers string return type), contextual typing (array.map(x => x.length) // infers parameter types), and best common type analysis (let mixed = [1, 'hello', true]; // infers string | number | boolean). TypeScript's inference engine considers assignments, return statements, expressions, and control flow to determine appropriate types. Inference maintains type safety while reducing the need for explicit type annotations, making code more concise while preserving all type checking benefits.
TypeScript inference follows several algorithms: (1) Best Common Type - selects the most specific type that encompasses all values in an array or object literal, (2) Contextual Typing - infers types based on where a value is used (function parameters, array methods), (3) Control Flow Analysis - refines types based on code paths and conditions, (4) Generic Type Inference - deduces generic types from function calls. Example: function identity
Mapped types transform existing types by creating new types that iterate over the keys of an original type. They use the syntax { [K in Keys]: Type } where Keys is typically keyof T for iterating over all properties of type T. Mapped types can modify property modifiers (add/remove readonly, optional) and transform property types. Examples: type Readonly
Regular const declarations create runtime immutability (variables cannot be reassigned), while 'as const' assertions create compile-time immutability (types become literal and readonly). const let x = 3; // x can be reassigned to any number; 'as const': const obj = { x: 10 } as const; // obj.x is exactly 10, not number. Key differences: const affects runtime behavior and prevents reassignment, 'as const' affects the type system only and prevents type widening. 'as const' effects: literals don't widen (3 stays 3, not number), arrays become readonly tuples, object properties become readonly, nested objects get deep readonly treatment. Example: const colors = ['red', 'green', 'blue'] as const; // type: readonly ['red', 'green', 'blue']. Use 'as const' for enum-like behavior, exact literal types, and when TypeScript's inference is too general. Use regular const for variable immutability where flexible types are desired.
Conditional types select types based on conditions using syntax similar to ternary operators: T extends U ? X : Y. They enable type-level programming and sophisticated type transformations. The extends keyword checks if type T is assignable to type U, returning type X if true, Y if false. Conditional types work with generics to create flexible type relationships. The 'infer' keyword extracts type information from within conditional types. Built-in examples include NonNullable
'strictNullChecks': true in tsconfig.json makes null and undefined distinct types that cannot be assigned to non-nullable types without explicit checking. Without this option, null and undefined are assignable to any type, leading to potential runtime null reference errors. With strictNullChecks enabled, you must explicitly handle null/undefined possibilities through union types (string | null), optional chaining (?.), nullish coalescing (??), or type guards. This catches the most common JavaScript runtime error ('Cannot read property of undefined') at compile time. It enforces null safety patterns and eliminates entire classes of bugs. Essential for production code quality, especially in applications dealing with API responses, user input, or external data. Modern TypeScript development standard that significantly improves code reliability.
Template literal types build string types by combining string literals, variables, and operations using template literal syntax (backticks with ${} placeholders). They enable type-safe string manipulation at compile time. Syntax: type EventNameon${Capitalize<T>}Changed; Example: type ClickEvent = EventName<'click'>; // 'onClickChanged'. Combine with unions for comprehensive string patterns: type HttpMethod = 'GET' | 'POST'; type ApiEndpoint/${T}; type UserApi = ApiEndpoint<'users'>; // '/users'. Built-in string manipulation types: Uppercase
Declaration files (.d.ts) provide type information for JavaScript code without containing implementation details. They describe the 'shape' of JavaScript libraries, enabling TypeScript to understand and type-check code that uses those libraries. Declaration files contain only type declarations using the 'declare' keyword and cannot include executable code. They enable TypeScript/JavaScript interoperability, providing autocomplete, error checking, and documentation for JavaScript libraries. The DefinitelyTyped repository (@types packages on npm) provides declaration files for most popular JavaScript libraries. Declaration files can be automatically generated from TypeScript source code or written manually for existing JavaScript. Essential for using JavaScript libraries in TypeScript projects, sharing type definitions across projects, and gradually migrating JavaScript codebases to TypeScript.
Index signatures define types for objects with dynamic property names, allowing access to properties that aren't known at compile time. Syntax: { [key: string]: Type } or { [index: number]: Type }. Key types must be string, number, or symbol. Example: interface StringMap { [key: string]: string; } const map: StringMap = { name: 'John', age: '30' };. All properties must conform to the signature type, including known properties. Use cases: dictionaries, JSON objects, configuration objects, API response data, and any object with dynamic keys. Alternative: Record<string, Type> utility type. Prefer specific properties with optional modifiers when property names are known. Consider Map for true dictionary functionality with better performance and methods. Index signatures are essential when working with data from external sources where property names aren't predetermined.
'never' represents values that never occur, functioning as TypeScript's bottom type. It's used to indicate impossible states or unreachable code paths. Properties: 'never' is assignable to every type, but no type (except 'never' itself) is assignable to 'never'. Use cases: (1) Functions that never return (always throw or have infinite loops): function throwError(message: string): never { throw new Error(message); }, (2) Exhaustive checking in switch statements with discriminated unions: default case with 'never' ensures all cases are handled, (3) Impossible types: string & number = never, (4) Type guards that always return false: function isNever(val: never): val is never { return false; }. The 'never' type helps ensure code correctness by making impossible states unrepresentable. Essential for type-safe state machines, exhaustive pattern matching, and maintaining type safety in complex conditional logic.
Abstract classes and interfaces serve different purposes: Abstract classes: cannot be instantiated directly, can contain implementation, support access modifiers (private, protected, public), can have constructors, enable single inheritance, are runtime entities, and provide shared code implementation. Interfaces: pure contracts with no implementation, all members are implicitly public, support multiple inheritance (extends multiple interfaces), are compile-time only (erased in generated JavaScript), cannot have constructors, and define only shape/structure. Use abstract classes when: sharing implementation code, needing protected members, requiring constructor logic, or building inheritance hierarchies. Use interfaces when: defining pure contracts, needing multiple inheritance, working with structural typing, or creating public APIs. Abstract classes are is-a relationships; interfaces are can-do relationships. Choose based on whether you need shared implementation (abstract class) or just shape definition (interface).
Discriminated unions combine literal types with unions to enable type-safe handling of different object shapes. Requirements: (1) A common property in all union members (the discriminant/tag), (2) The discriminant must be a literal type (string, number, boolean literal), (3) A union of the different object types. TypeScript narrows the type based on the discriminant value. Example: type Result = { success: true, data: T } | { success: false, error: string }; function handleResult(result: Result) { if (result.success) { console.log(result.data); // TypeScript knows this exists } else { console.log(result.error); // TypeScript knows this exists } }. The discriminant acts as a type tag that uniquely identifies each variant. Use cases: API responses (success/error states), Redux actions, state machines, configuration objects, and any scenario with mutually exclusive object shapes. Eliminates runtime type checking and enables exhaustive checking with 'never' type. Essential for type-safe state management.
'keyof' creates a union type of an object type's property names. Syntax: keyof Type. Example: interface User { id: number; name: string; } type UserKeys = keyof User; // 'id' | 'name'. 'keyof' works with classes, interfaces, and type aliases. It's commonly combined with indexed access types (T[K]) for type-safe property access: function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; }. 'keyof' respects property modifiers (readonly, optional) and works with mapped types. Use cases: generic property access, building type-safe object utilities, constraint validation, dynamic property manipulation, and creating flexible APIs that work with object properties. 'keyof' maintains type safety when working with dynamic property names and enables powerful type-level programming patterns. Essential for building reusable, type-safe utilities that operate on object properties.
Namespaces are TypeScript's way to group related code and prevent global scope pollution using the namespace Name { } syntax. Formerly called 'internal modules' in early TypeScript. Namespaces create nested object structures at runtime that can contain classes, interfaces, functions, and variables. They support multi-file organization using reference comments (/// <reference path='otherFile.ts' />) and can be split across multiple files. Namespaces are compiled into JavaScript objects that maintain the nested structure. Example: namespace MathUtils { export function add(a: number, b: number): number { return a + b; } } compiles to var MathUtils; (function(MathUtils) { MathUtils.add = function(a, b) { return a + b; }; })(MathUtils || (MathUtils = {}));. Namespaces are mainly used today for declaration files and legacy code organization.
Modules are TypeScript's implementation of ES6 modules, providing file-based code organization using import and export statements. Each file with imports/exports is automatically a module with its own scope. Modules support named exports (export const PI = 3.14;), default exports (export default class Calculator {}), and re-exports (export * from './utils';). Modules can import specific items (import { add } from './math';) or entire modules (import * as Math from './math';). They enable tree-shaking (elimination of unused code), better bundling with webpack/Rollup, and align with modern JavaScript standards. Example: // math.ts export function add(a, b) { return a + b; } // main.ts import { add } from './math'; console.log(add(2, 3));. Modules are the recommended approach for organizing TypeScript code.
Key differences: Scope - Namespaces: global scope management with nested objects; Modules: file-based scope with proper isolation. Runtime - Namespaces: emit nested JavaScript objects; Modules: emit separate files with import/export. Tooling - Namespaces: limited tree-shaking, older bundler support; Modules: excellent tree-shaking, modern bundler optimization. Usage - Namespaces: legacy TypeScript, declaration files, global utilities; Modules: preferred for all new code, standard JavaScript approach. Dependencies - Namespaces: use reference comments for multi-file; Modules: use import/export with module resolution. Performance - Modules generally better due to smaller bundles and better code splitting. Modern TypeScript strongly favors modules over namespaces for better maintainability, tooling support, and alignment with JavaScript ecosystem standards.
Assertion functions validate conditions and narrow types within their scope, throwing errors if conditions aren't met. Two forms: (1) asserts condition: function assert(condition: unknown, message?: string): asserts condition; (2) asserts parameter is Type: function isString(value: unknown): asserts value is string. When an assertion function executes successfully, TypeScript assumes the asserted condition is true for all subsequent code. Example: function assertString(val: unknown): asserts val is string { if (typeof val !== 'string') throw new Error('Not a string'); } function process(val: unknown) { assertString(val); console.log(val.toUpperCase()); // TypeScript knows val is string }. Built-in: console.assert in Node.js. Use cases: input validation, preconditions, invariants, runtime type checking. Assertion functions provide cleaner syntax than type guards when you want to throw on invalid input rather than return false. TypeScript 3.7+ feature essential for runtime type validation.
Essential tsconfig.json compiler options: target - specifies ECMAScript version (ES2020, ESNext), module - module system (ESNext, CommonJS, AMD), strict - enables all strict type checking options, outDir - output directory for compiled files, rootDir - root directory of input files, include/exclude - file selection patterns, lib - built-in type definitions (ES2020, DOM), moduleResolution - strategy for resolving modules (node, bundler), esModuleInterop - enables CommonJS/ES Module interop, skipLibCheck - skip type checking of declaration files, resolveJsonModule - allows importing JSON files, declaration - generate .d.ts files, sourceMap - generate source maps for debugging, allowSyntheticDefaultImports - consistent default import behavior. Start with 'tsc --init' for defaults. Use 'strict: true' for new projects. Adjust target/module based on runtime environment. Project references for monorepos with multiple related projects.
General
2 questionsFor TypeScript projects, implement a multi-layered approach to secrets detection and file access control: (1) Use pre-commit hooks with tools like Gitleaks (lightweight, fast, customizable rules), TruffleHog (classifies 800+ secret types, validates if secrets are live), or detect-secrets (minimizes false positives for production). Gitleaks and TruffleHog found 1,533 and 438 non-overlapping unique secrets respectively in research, demonstrating the value of using multiple tools. (2) Detect secrets in real-time as code is pushed, using pattern matching, regular expressions, and entropy analysis to identify random/encrypted character sequences. (3) Store secrets in environment variables or secrets management tools (HashiCorp Vault, AWS Secrets Manager, Google Cloud Secret Manager) instead of hardcoding. (4) Add .gitignore entries for sensitive files and integrate secret scanning into CI/CD pipelines. For file access control: (1) Use Node.js Permission Model with --permission flag to restrict file system access; grant specific access via --allow-fs-read and --allow-fs-write flags (e.g., --allow-fs-read=/home/test* for wildcard access). (2) Use fs.access() with mode constants (fs.constants.F_OK for existence, R_OK for read, W_OK for write, X_OK for execute) to test permissions, but open/read/write files directly and handle errors rather than checking access first to avoid race conditions. (3) Use fs.chmod() to modify permissions with octal numbers (e.g., 0o400 for read-only owner). (4) Implement TypeScript access modifiers (private, protected, public) to restrict class member access. (5) Always validate and sanitize user input that interacts with file systems.
To detect API keys, passwords, and secrets in code files and prevent unauthorized access, use automated secret scanning tools with pre-commit hooks and CI/CD integration. The three primary open-source tools are: (1) TruffleHog - detects and verifies over 800 secret types by checking credentials against actual SaaS provider APIs, scans git repositories, Docker images, AWS S3, and filesystems (GitHub: trufflesecurity/trufflehog); (2) git-secrets - AWS Labs tool that installs git hooks to prevent commits containing secrets, specifically checks for AWS Access Key IDs, Secret Access Keys, and account IDs (GitHub: awslabs/git-secrets); (3) detect-secrets - Yelp's enterprise tool using regex patterns and a baseline file approach to identify new secrets in diff outputs without scanning entire git history (GitHub: Yelp/detect-secrets). Detection methods include pattern matching for common formats (API keys, tokens), entropy analysis for high-randomness strings, and machine learning for non-standard patterns. Best practices: store secrets in dedicated management tools (HashiCorp Vault, AWS Secrets Manager) that encrypt at rest and in transit, implement pre-commit hooks to block secrets before commit, integrate continuous scanning in CI/CD pipelines, use environment variables instead of hardcoding, rotate secrets regularly, implement access controls and audit logging, and adopt shift-left security strategy. GitHub announced AI-powered secret detection using Copilot (July 2025) for unstructured secrets. 61% of organizations have exposed secrets in public repositories, making automated detection critical for preventing unauthorized access to databases, cloud infrastructure, and sensitive systems.