| technology | TypeScript | |||||
|---|---|---|---|---|---|---|
| domain | frontend | |||||
| level | Senior/Architect | |||||
| version | 5.5+ | |||||
| tags |
|
|||||
| ai_role | Senior TypeScript Architecture Expert | |||||
| last_updated | 2026-03-29 |
Note
Context: Handling data of an uncertain type. any disables all type-checking, while unknown forces safety.
function process(data: unknown) {
console.log(data.name); // No error, but might crash at runtime
}any is a "get out of jail free" card that propagates through the codebase, effectively turning off TypeScript's benefits and hiding potential runtime exceptions.
function process(data: unknown) {
if (data && typeof data === 'object' && 'name' in data) {
console.log((data as { name: string }).name);
}
}Use unknown for values whose type is not yet determined. It requires a type check or assertion before usage, ensuring the developer acknowledges the data's structure.
Note
Context: Distinguishing between "value not provided" and "value is empty."
interface UserResponse {
bio: string | null | undefined;
}Using both creates ambiguity. In JSON, undefined properties are often stripped, while null is preserved. Mixing them increases complexity in conditional checks.
interface UserResponse {
bio?: string | null; // Optional if missing, null if explicitly empty
}Standardize: use undefined (optional properties) for missing keys and null for intentional absence of value. Avoid using both for the same field unless strictly required by a legacy API.
Note
Context: Visual consistency in array declarations.
const users: Array<User> = [];
const complex: Array<string | number> = [];Array<T> is more verbose and can be confused with other generic types. It is harder to scan in complex signatures.
const users: User[] = [];
const complex: (string | number)[] = [];Prefer the shorthand T[]. It is idiomatic, more readable, and clearly distinguishes arrays from other generic containers like Record or Promise.
Note
Context: Defining object structures and aliases.
type Point = { x: number; y: number; }; // Bad: Using type for object structure
interface Status { status: "active" | "inactive"; } // Bad: Trying to use interface for a union-like structureUsing type for object structures prevents declaration merging and reduces performance in TS compiler caching. Using interface for unions is impossible or leads to awkward wrapper objects.
interface Point { x: number; y: number; }
type Status = "active" | "inactive";Important
Prefer interface for structure, type for unions. Interfaces provide better error messages and performance for structural types in TypeScript 5.x.
Logical Conflict Resolution: To enforce the repo standard, NEVER use type for defining object structures, and NEVER use interface for unions.
Note
Context: Handling functions with different input/output combinations.
function format(input: string): string;
function format(input: number): string;
function format(input: unknown): string {
return String(input);
}Overloads are verbose and can be harder to implement correctly. They often require any or complex type-casting in the implementation body.
function format(input: string | number): string {
return String(input);
}Prefer Union types when the implementation logic is identical for all types. Reserve overloads only for cases where the return type strictly depends on the input type and cannot be expressed via generics.
Note
Context: Organizing code in the ES Module era.
namespace Utils {
export const log = (msg: string) => console.log(msg);
}Namespaces are a legacy TypeScript feature. They don't play well with modern bundlers (Tree Shaking), are harder to test, and can lead to naming collisions in the global scope.
// utils.ts
export const log = (msg: string) => console.log(msg);Use ES Modules (export/import). They are the industry standard, supported by all modern environments, and allow for better static analysis.
Note
Context: Grouping related constants.
enum Status {
Active,
Inactive
}Enums generate extra runtime code and have "reverse mapping" behavior that can lead to bugs (e.g., Status[0] returns "Active"). They also don't align with "TypeScript as a type-only layer."
const STATUS = {
ACTIVE: 'active',
INACTIVE: 'inactive'
} as const;
type Status = typeof STATUS[keyof typeof STATUS];Use const objects with as const and a derived union type. This is more predictable, emits cleaner code, and is easier to iterate over.
Please refer to the specialized guides for detailed best practices:
For further reading, please refer to the following specialized guides: