namespace Validation {
export function isValid(input: string): boolean {
return input.length > 0;
}
}
Validation.isValid('hello'); // true
Topics
26
Classes: Access Modifiers, Abstract Classes & Implements
Conditional Types
Declaration Files
Decorators
Discriminated Unions & Exhaustiveness Checking
Enums
Function Types, Overloads & Optional/Default Parameters
Generics
Index Signatures & Record Types
Mapped Types
Migrating JavaScript to TypeScript
Modules
Namespaces & Declaration Merging
Never, Unknown & Void Types
Nullability: strictNullChecks, Optional Chaining & Nullish Coalescing
Primitive Types, Arrays & Tuples
Readonly, const Assertions & Immutability
Structural Typing & Duck Typing
Template Literal Types
tsconfig & Compiler Options
Type Assertions & Type Casting
Type Inference & Contextual Typing
Type Narrowing & Guards
Types & Interfaces
Union & Intersection Types
Utility Types
Namespaces & Declaration Merging
10 questions found
A namespace groups related code under a single named container using the 'namespace' keyword, generating a nested object at runtime — it was TypeScript's original way to organize code before ES Modules became the standard, and is mostly used today for organizing types or in non-module global scripts.
Real-world example
Organizing a set of related global type definitions in a non-module ambient .d.ts file.
Modules
Just like a module, everything declared inside a namespace is private to that namespace by default; only explicitly 'export'ed members become accessible via NamespaceName.memberName from outside code.
namespace MathUtils {
function internalHelper() { return 1; } // not accessible outside
export function add(a: number, b: number) { return a + b + internalHelper(); }
}
MathUtils.add(2, 3); // works
// MathUtils.internalHelper(); // Error: not exported
Real-world example
Hiding internal helper functions within a namespace while exposing only its intended public API.
Modules
What is declaration merging, and how does TypeScript combine multiple 'interface' declarations with the same name?
IntermediateWhen you declare an interface with the same name multiple times (even in different files), TypeScript automatically MERGES all their members into a single combined interface, rather than treating the second declaration as an error or an override — this is unique to interfaces (type aliases do NOT support this).
interface Window {
myCustomProp: string;
}
interface Window {
anotherProp: number;
}
// merged result: Window now has both myCustomProp AND anotherProp
Real-world example
Extending a built-in global interface (like Window or Express's Request) with additional properties from a separate file.
Declaration Files
How does merging work between a namespace and a class (or function) sharing the same name?
IntermediateA namespace can merge with a class, function, or enum of the same name declared alongside it, effectively attaching static-like members or nested types to that class/function/enum — this pattern was TypeScript's original mechanism for expressing things like static-nested-types before more modern syntax existed.
class Album {
title: string = '';
}
namespace Album {
export function create(title: string): Album {
const a = new Album();
a.title = title;
return a;
}
}
Album.create('OK Computer'); // uses the merged namespace member
Real-world example
Attaching a static factory-like namespace of helper functions directly onto a class, without those helpers being instance methods.
Modules
How do you merge a namespace with an enum to add extra static-like helper functionality to that enum?
IntermediateDeclare a namespace with the same name as the enum, and add exported functions inside it — TypeScript merges the namespace's members onto the enum object, letting you attach utility functions (like validation or display formatting) directly to the enum's name.
enum Color { Red, Green, Blue }
namespace Color {
export function isValid(value: number): boolean {
return value in Color;
}
}
Color.isValid(1); // true, uses the merged namespace function
Real-world example
Adding a helper function like isValid() or toDisplayName() directly onto an enum's own namespace, rather than as a separate standalone function.
Enums
What conflicts and restrictions exist when merging two interfaces that both declare the same property name?
AdvancedIf both interface declarations declare the SAME property name with the SAME type, they merge without conflict; if they declare it with DIFFERENT, incompatible types, TypeScript raises a compile error — merging only works cleanly when the overlapping members are structurally compatible across all declarations.
interface Config { timeout: number; }
interface Config { timeout: number; retries: number; } // OK, 'timeout' matches
// interface Config { timeout: string; } // Error: subsequent property declaration must be of type 'number'
Real-world example
Debugging a confusing 'subsequent property declarations must have the same type' error when two teams accidentally declare overlapping global interfaces differently.
Types & Interfaces
How would you use declaration merging to properly type a module augmentation that adds a method to a third-party library's exported class?
AdvancedCombine 'declare module' (to reopen the library's module) with an interface declaration matching the class's name inside it — TypeScript merges your added interface members onto the library's own class type, so instances gain the new property/method in the type system (though you're still responsible for ACTUALLY implementing that behavior at runtime, e.g. via monkey-patching or a plugin system).
// dayjs-plugin.d.ts
import 'dayjs';
declare module 'dayjs' {
interface Dayjs {
fromNow(): string; // added by a plugin like dayjs/plugin/relativeTime
}
}
// usage.ts
import dayjs from 'dayjs';
dayjs().fromNow(); // now type-checks correctly
Real-world example
Writing plugin type augmentations for a library like Day.js, Vue, or Express whose plugins add real runtime methods to existing classes.
Declaration Files
How does namespace merging across multiple files work, and what does this mean for organizing large ambient type declarations?
AdvancedMultiple files can each contribute a piece of the SAME namespace (using 'namespace Foo { ... }' repeated across files, all included in the compilation), and TypeScript merges all their exported members into one logical namespace — useful for splitting a large set of ambient global types across multiple organized files while still presenting a single unified namespace to consumers.
// file1.d.ts
namespace MyLib {
export interface Config { name: string; }
}
// file2.d.ts
namespace MyLib {
export interface Options { verbose: boolean; }
}
// Both Config and Options are accessible as MyLib.Config and MyLib.Options
Real-world example
Organizing a large third-party library's ambient global type declarations across multiple logically-grouped files.
Declaration Files
Why can't 'type' aliases participate in declaration merging the way 'interface' declarations can, and what design implication does this have?
AdvancedType aliases are meant to bind a NAME to a single, specific type expression once — allowing the same name to be redeclared with a different type expression would be ambiguous and error-prone (which expression 'wins'?), so TypeScript intentionally disallows it and raises a 'Duplicate identifier' error, unlike interfaces which are explicitly designed around incremental, additive extension.
type Config = { name: string };
// type Config = { retries: number }; // Error: Duplicate identifier 'Config'
interface IConfig { name: string; }
interface IConfig { retries: number; } // OK: interfaces merge
Real-world example
Deciding to use 'interface' instead of 'type' specifically because a type needs to support later extension via declaration merging, like a global augmentation target.
Types & Interfaces
How would you migrate a legacy namespace-organized codebase to ES Modules, and what pitfalls commonly arise?
AdvancedConvert each namespace block into a module file with normal 'export' statements (removing the namespace wrapper syntax), replace namespace-qualified references (NamespaceName.member) with proper import statements, and watch for previously-implicit global availability (namespaces often relied on being loaded via <script> tags in a specific order) that now needs explicit import/export wiring.
// Before: namespace-based (implicit global load order matters)
namespace Utils {
export function formatDate(d: Date): string { /* ... */ }
}
// After: ES Module
// utils.ts
export function formatDate(d: Date): string { /* ... */ }
// consumer.ts
import { formatDate } from './utils';
Real-world example
Modernizing a legacy TypeScript codebase (originally built before ES Modules were standard) to use proper module imports/exports.
Modules