A function should behave as its name implies. There should be no hidden behavior.
Key Patterns
Reveal Hidden Logic
❌ Hidden side effects in the fetch function:
async function fetchBalance(): Promise<number> {
const balance = await http.get<number>("...");
logging.log("balance_fetched"); // 숨은 부수 효과!
return balance;
}
✅ Side effects explicitly at the call site:
async function fetchBalance(): Promise<number> {
return await http.get<number>("...");
}
const balance = await fetchBalance();
logging.log("balance_fetched");
Unify Return Types
❌ Same kind but different return types:
function checkIsNameValid(name: string) {
return name.length > 0; // boolean
}
function checkIsAgeValid(age: number) {
if (age < 18) return { ok: false, reason: "18세 이상" };
return { ok: true }; // 객체
}
✅ Unify with a Discriminated Union:
type ValidationResult = { ok: true } | { ok: false; reason: string };
function checkIsNameValid(name: string): ValidationResult {
if (name.length === 0) return { ok: false, reason: "이름 필수" };
return { ok: true };
}
function checkIsAgeValid(age: number): ValidationResult {
if (age < 18) return { ok: false, reason: "18세 이상" };
return { ok: true };
}
Quick Reference
| Code Smell | How to Improve |
|---|---|
getX()has side effects |
Separate pure getters and actions |
calculateX()stores |
calculateAndSaveX()rename to or split into |
| same kind but different return types | Discriminated Union: { ok: true } | { ok: false; reason } |
| Conflicts with library name | Use a distinct name: http → httpService |
Cautions
- Don't remove side effects; move them to an explicit location
- Avoid overly complex types
- Maintain the existing API contract
Reference: https://frontend-fundamentals.com/code-quality/predictable/