'never' íì ì ëí ì¢ í©ì ìž ê°ìŽëì ëë€. ê°ë ¥íê³ ë²ê·ž ìë ìœë륌 ìíŽ ìì í ê²ì¬ë¥Œ íì©íê³ ì íµì ìž ì€ë¥ ì²ëЬìì êŽê³ë¥Œ ìŽíŽíë ë°©ë²ì ë°°ì°ìžì.
The Never Type: Shifting from Runtime Errors to Compile-Time Guarantees
ìíížìšìŽ ê°ë° ìžê³ìì ì°ëЬë ë²ê·žë¥Œ ìë°©íê³ , ì°Ÿê³ , ìì íë ë° ìë¹í ìê°ê³Œ ë žë ¥ì ììµëë€. ê°ì¥ ìíí ë²ê·ž ì€ ìŒë¶ë ì¡°ì©í ëíëë ë²ê·žì ëë€. ì í늬ìŒìŽì ì ìŠì ì¶©ëìí€ì§ ìê³ ì²ëЬëì§ ìì ì£ì§ ìŒìŽì€ì ìšìŽ í¹ì ë°ìŽí° ì¡°ê°ìŽë ì¬ì©ì ìì ìŽ ì못ë ëìì ížëŠ¬ê±°íꞰ륌 êž°ë€ëŠœëë€. ìŽë¬í ë²ê·žì ìŒë°ì ìž ììžì ê°ëší ê°ê³Œì ëë€. ê°ë°ìê° ì í ì¬í ìžížì ì ìµì ì ì¶ê°íì§ë§ íŽë¹ ìµì ì ì²ëЬíŽìŒ íë ìœëì 몚ë ìì¹ë¥Œ ì ë°ìŽížíë ê²ì ììŽë²ëЬë ê²ì ëë€.
`switch` ë¬žìŽ ì¬ë¬ ì íì ì¬ì©ì ì늌ì ì²ëЬíë€ê³ ê°ì íŽ ë³Žê² ìµëë€. ì ì늌 ì í(ì: 'POLL_RESULT')ìŽ ì¶ê°ë ë ì늌 ë ëë§ íšììì íŽë¹ `case` ëžë¡ì ì¶ê°íë ê²ì ììŽë²ëŠ¬ë©Ž ìŽë»ê² ë ê¹ì? ë§ì ìžìŽìì ìœëë ëšìí ì€íšíê³ ì묎 ê²ë íì§ ìê³ ì¡°ì©í ì€íší©ëë€. ì¬ì©ìë ì¬ë¡ ì¡°ì¬ ê²°ê³Œë¥Œ ë³Žì§ ëª»íê³ ëª ì£Œ ëì ë²ê·žë¥Œ ë°ê²¬íì§ ëª»í ì ììµëë€.
컎íìŒë¬ê° ìŽë¥Œ ë°©ì§í ì ìë€ë©Ž ìŽë»ê² ë ê¹ì? ì°ëЬ ìì ì ëêµ¬ê° ëªšë ê°ë¥ì±ì ì²ëЬíëë¡ ê°ì íì¬ ì ì¬ì ìž ë°íì ë ŒëŠ¬ ì€ë¥ë¥Œ 컎íìŒ íì ì í ì€ë¥ë¡ ë°ê¿ ì ìë€ë©Ž ìŽë»ê² ë ê¹ì? ìŽê²ìŽ ë°ë¡ ìµì ì ì íì ìžìŽìì ë°ê²¬ëë ê°ë ìž 'never' íì ìŽ ì ê³µíë íì ëë€. ìŽë 몚ë ìŒìŽì€ê° ì²ëЬëëë¡ ê°ë ¥í 컎íìŒ íì 볎ìŠì ì ê³µíë ìì í ê²ì¬ë¥Œ ìííë ë©ì»€ëìŠì ëë€. ìŽ êž°ì¬ììë `never` íì ì ìŽíŽë³Žê³ , ì íµì ìž ì€ë¥ ì²ëЬìì ìí ì ë¹êµíê³ , ìŽë¥Œ ì¬ì©íì¬ ë³Žë€ íë ¥ì ìŽê³ ì ì§ êŽëŠ¬ê° ì©ìŽí ìíížìšìŽ ìì€í ì 구ì¶íë ë°©ë²ì 볎ì¬ì€ëë€.
What Exactly is the 'Never' Type?
ìžë» 볎Ʞì `never` íì ì ëíŽíê±°ë ìì í í묞ì ìŒë¡ ë³ŽìŒ ì ììµëë€. ê·žë¬ë ì€ì ì ìž ì믞ë ì¬ì€í©ëë€. ìŽë¥Œ ìŽíŽíë €ë©Ž ë ê°ì§ 죌ì í¹ì±ì íì íŽìŒ í©ëë€.
A Type for the Impossible
`never` íì ì ì ë ë°ìí ì ìë ê°ì ëíë ëë€. ê°ë¥í ê°ìŽ ìë íì ì ëë€. ìŽê²ì ì¶ìì ìŒë¡ ë€ëЬì§ë§ ë€ì곌 ê°ì ë ê°ì§ 죌ì ìë늬ì€ë¥Œ ëíëŽë ë° ì¬ì©ë©ëë€.
- ì ë ë°ííì§ ìë íšì: ìŽê²ì ì묎 ê²ë ë°ííì§ ìë íšì(ê·žê²ì `void`ì ëë€)륌 ì믞íì§ ììµëë€. ëì ì ëë¬íì§ ìë íšì륌 ì믞í©ëë€. ì€ë¥ë¥Œ ë°ììí€ê±°ë 묎í 룚íì ë€ìŽê° ì ììµëë€. ì€ìí ê²ì ì ìì ìž ì€í íëŠìŽ ì구ì ìŒë¡ ì€ëšëë€ë ê²ì ëë€.
- ë¶ê°ë¥í ìíì ë³ì: ë ŒëŠ¬ì ì¶ë¡ (íì ì¢íêž°ëŒê³ íš)ì íµíŽ ì»ŽíìŒë¬ë ë³ìê° í¹ì ìœë ëžë¡ ëŽìì ê°ì ê°ì§ ì ìë€ê³ íëší ì ììµëë€. ìŽ ìí©ìì ë³ìì íì ì ì¬ì€ì `never`ì ëë€.
íì ìŽë¡ ìì `never`ë ë°ë¥ íì (ì¢ ì¢ â¥ë¡ íìëš)ìŒë¡ ìë €ì ž ììµëë€. ë°ë¥ íì ìŽëŒë ê²ì ë€ë¥ž 몚ë íì ì íì íì ìì ì믞í©ëë€. ìŽê²ì ìŽì¹ì ë§ìµëë€. `never` íì ì ê°ì 졎ì¬í ì ììŒë¯ë¡ íì ìì ì±ì ìë°íì§ ìê³ `string`, `number` ëë `User` íì ì ë³ìì í ë¹í ì ììµëë€. ìëí멎 íŽë¹ ìœë ì€ì ìŠëª ê°ë¥íê² ëë¬í ì ìêž° ë묞ì ëë€.
Crucial Distinction: `never` vs. `void`
ìŒë°ì ìž íŒëì ì§ì ì `never`ì `void`ì ì°šìŽì ì ëë€. ê·ž ì°šìŽë ë§€ì° ì€ìí©ëë€.
void: ì¬ì© ê°ë¥í ë°í ê°ìŽ ììì ëíë ëë€. íšìë ìë£ë ëê¹ì§ ì€íëê³ ë°íëì§ë§ ë°í ê°ì ì¬ì©ëì§ ììµëë€. ìœìì êž°ë¡íë íšì륌 ìê°íŽ ë³Žììì€.never: ë°íì ë¶ê°ë¥ì±ì ëíë ëë€. íšìë ì ìì ìŒë¡ ì€í 겜ë¡ë¥Œ ìë£íì§ ììì 볎ì¥í©ëë€.
TypeScript ìì 륌 ìŽíŽ ë³Žê² ìµëë€.
// This function returns 'void'. It completes successfully.
function logMessage(message: string): void {
console.log(message);
// Implicitly returns 'undefined'
}
// This function returns 'never'. It never completes.
function throwError(message: string): never {
throw new Error(message);
}
// This function also returns 'never' due to an infinite loop.
function processTasks(): never {
while (true) {
// ... process a task from a queue
}
}
ìŽ ì°šìŽì ì ìŽíŽíë ê²ìŽ `never`ì ì€ì íì ì¬ë 첫 ë²ì§ž ëšê³ì ëë€.
The Core Use Case: Exhaustive Checking
`never` íì ì ê°ì¥ ìí¥ë ¥ìë ì í늬ìŒìŽì ì 컎íìŒ íìì ìì í ê²ì¬ë¥Œ ìííë ê²ì ëë€. ìŽë¥Œ íµíŽ ì£ŒìŽì§ ë°ìŽí° íì ì 몚ë ë³íì ì²ëЬíëì§ íìžíë ìì ë§ì êµ¬ì¶ í ì ììµëë€.
The Problem: The Fragile `switch` Statement
íë³ë ì ëìšì ì¬ì©íì¬ êž°ííì ëí ìžížë¥Œ 몚ëžë§íŽ ë³Žê² ìµëë€. ìŽë ë€ë£šë íì ì ë³íì ìë €ì£Œë ê³µíµ ìì±( 'discriminant', ì륌 ë€ìŽ `kind`)ì ê°ì§ ê°ë ¥í íšíŽì ëë€.
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'square'; sideLength: number };
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'square':
return shape.sideLength ** 2;
}
// What happens if we get a shape we don't recognize?
// This function would implicitly return 'undefined', a likely bug!
}
ìŽ ìœëë íì¬ ìëí©ëë€. íì§ë§ ì í늬ìŒìŽì ìŽ ì§íí멎 ìŽë»ê² ë ê¹ì? ëë£ê° ìë¡ìŽ ëíì ì¶ê°í©ëë€.
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'square'; sideLength: number }
| { kind: 'rectangle'; width: number; height: number }; // New shape added!
`getArea` íšìë ìŽì ë¶ìì í©ëë€. `rectangle`ì ë°ìŒë©Ž `switch` 묞ì ìŒì¹íë ìŒìŽì€ê° ìê³ íšìê° ìë£ëë©° JavaScript/TypeScriptìì `undefined`ê° ë°íë©ëë€. ížì¶ ìœëë `number`륌 ììíì§ë§ `undefined`륌 ê°ì žì `NaN` ì€ë¥ ëë ë€ë¥ž 믞ë¬í ë²ê·žê° ë°ìí©ëë€. 컎íìŒë¬ë ê²œê³ ë¥Œ 볎ëŽì§ ìììµëë€.
The Solution: The `never` Type as a Safeguard
`switch` 묞ì `default` ìŒìŽì€ìì `never` íì ì ì¬ì©íì¬ ìŽ ë¬žì 륌 íŽê²°í ì ììµëë€. ìŽ ê°ëší ì¶ê°ë 컎íìŒë¬ë¥Œ ì°ëЬì 겜ê³íë íížëë¡ ë³íìíµëë€.
function getAreaWithExhaustiveCheck(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'square':
return shape.sideLength ** 2;
// What about 'rectangle'? We forgot it.
default:
// This is where the magic happens.
const _exhaustiveCheck: never = shape;
// The line above will now cause a compile-time error!
// Type 'Rectangle' is not assignable to type 'never'.
return _exhaustiveCheck;
}
}
ìŽê²ìŽ ìëíë ìŽì 륌 ë¶ìíŽ ë³Žê² ìµëë€.
- íì ì¢íêž°: ê° `case` ëžë¡ ëŽë¶ìì TypeScript 컎íìŒë¬ë `shape` ë³ìì íì ì ì¢íë§íŒ ëëí©ëë€. `case 'circle'`ìì 컎íìŒë¬ë `shape`ê° `{ kind: 'circle'; radius: number }`ìì ìê³ ììµëë€.
- `default` ëžë¡: ìœëê° `default` ëžë¡ì ëë¬í멎 컎íìŒë¬ë `shape`ê° ê°ì§ ììë íì ì ì¶ë¡ í©ëë€. ìë `Shape` ì ëìšìì ì²ëЬë 몚ë ìŒìŽì€ë¥Œ ëºëë€.
- ì€ë¥ ìë늬ì€: ì ë°ìŽíž ë ììì ì°ëЬë `'circle'`곌 `'square'`륌 ì²ëЬíìµëë€. ë°ëŒì `default` ëžë¡ ëŽë¶ìì 컎íìŒë¬ë `shape`ê° `{ kind: 'rectangle'; ... }` ì¬ìŒ íšì ìê³ ììµëë€. ê·žë° ë€ì ìœëë ìŽ `rectangle` ê°ì²Žë¥Œ `_exhaustiveCheck` ë³ìì í ë¹íë €ê³ ìëí©ëë€. ìŽ ë³ìë `never` íì ì ê°ì§ê³ ììµëë€. ìŽ í ë¹ì ëª íí íì ì€ë¥ë¡ ì€íší©ëë€. `Type 'Rectangle' is not assignable to type 'never'`. ìœëê° ì€íëêž° ì ì ë²ê·žê° ì¡íëë€!
- ì±ê³µ ìë늬ì€: `'rectangle'`ì ëí `case`륌 ì¶ê°í멎 `default` ëžë¡ìì 컎íìŒë¬ë 몚ë ê°ë¥ì±ì ì몚í©ëë€. `shape`ì íì ì `never`ë¡ ì¢íì§ëë€ (ì, ì¬ê°í ëë ì§ì¬ê°í ìŒ ì ììŒë¯ë¡ ë¶ê°ë¥í íì ì ëë€). `never` íì ì ê°ì `never` íì ì ë³ìì í ë¹íë ê²ì ì벜íê² ì íší©ëë€. ìœëë ì€ë¥ììŽ ì»ŽíìŒë©ëë€.
ìŽ íšíŽì ì¢ ì¢ "exhaustiveness trick"ìŽëŒê³ ë¶ëŠ¬ë©° 컎íìŒë¬ê° ìì ì±ì ìííëë¡ íšê³Œì ìŒë¡ ììí©ëë€. ê·žê²ì 깚ì§êž° ì¬ìŽ ë°íì ê·ì¹ì ê²¬ê³ í 컎íìŒ íì 볎ìŠìŒë¡ ë°ê¿ëë€.
Exhaustive Checking vs. Traditional Error Handling
ìì í ê²ì¬ë¥Œ ì€ë¥ ì²ëŠ¬ë¥Œ ë첎íë ê²ìŒë¡ ìê°íêž° ìœì§ë§ ê·žê²ì ì€íŽì ëë€. ê·žë€ì ìë¡ ë€ë¥ž ì¢ ë¥ì 묞ì 륌 íŽê²°íëë¡ ì€ê³ë 볎ìì ìž ë구ì ëë€. íµì¬ì ìž ì°šìŽì ì ììž¡ ê°ë¥íê³ ìë €ì§ ìíì ììž¡ ë¶ê°ë¥íê³ ììžì ìž ìŽë²€ížë¥Œ ì²ëЬíëë¡ ì€ê³ëìë€ë ì ì ììµëë€.
Defining the Concepts
-
ì€ë¥ ì²ëЬë ì¢
ì¢
íë¡ê·žëšì ì ìŽ ë²ì륌 ë²ìŽëë ììžì ìŽê³ ììž¡ ë¶ê°ë¥í ìí©ì êŽëЬíêž° ìí ë°íì ì ëµì
ëë€. ì€í ì€ì ë°ìí ì ìê³ ì€ì ë¡ ë°ìíë ì€íšë¥Œ ì²ëЬí©ëë€.
- ì: ë€ížìí¬ ìì² ì€íš, ëì€í¬ìì íìŒì ì°Ÿì ì ìì, ì못ë ì¬ì©ì ì ë ¥, ë°ìŽí°ë² ìŽì€ ì°ê²° ìê° ìŽê³Œ.
- ë구: `try...catch` ëžë¡, `Promise.reject()`, ì€ë¥ ìœë ëë `null` ë°í, `Result` íì (Rustì ê°ì ìžìŽìì 볌 ì ìì).
-
ìì í ê²ì¬ë íë¡ê·žëš ë¡ì§ ëŽìì 몚ë ìë €ì§ ì íší ë
ŒëЬì ê²œë¡ ëë ë°ìŽí° ìíê° ëª
ìì ìŒë¡ ì²ëЬëëë¡ ë³Žì¥íë 컎íìŒ íì ì ëµì
ëë€. ìœëì ìì ì±ì 볎ì¥íë ê²ì
ëë€.
- ì: ìŽê±°íì 몚ë ë³í ì²ëЬ, íë³ë ì ëìšì 몚ë íì ì²ëЬ, ì í ìí ëšžì ì 몚ë ìí êŽëЬ.
- ë구: `never` íì , ìžìŽê° ê°ì íë `switch` ëë `match` ìì ì± (Swift ë° Rustìì 볌 ì ìì).
The Guiding Principle: Knowns vs. Unknowns
ìŽë€ ì ê·Œ ë°©ìì ì¬ì©í ì§ ê²°ì íë ê°ëší ë°©ë²ì 묞ì ì 볞ì§ì ëíŽ ì€ì€ë¡ìê² ë¬»ë ê²ì ëë€.
- ìŽê²ìŽ ëŽ ìœëë² ìŽì€ ëŽìì ì ìíê³ ì ìŽíë ââê°ë¥ì± ì§í©ì ëê¹? ìì í ê²ì¬ë¥Œ ì¬ì©íììì€. ìŽê²ë€ì ë¹ì ì "knowns"ì ëë€. ë¹ì ì `Shape` ì ëìšì ì벜í ìì ëë€. ë¹ì ì ê°ë¥í 몚ë 몚ìì ì ìí©ëë€.
- ìŽê²ìŽ ìžë¶ ìì€í , ì¬ì©ì ëë í겜ìì ë°ìíë ìŽë²€ížì ëê¹? ì¬êž°ì ì€íšê° ê°ë¥íê³ ì íí ì ë ¥ì ììž¡í ì ììµëê¹? ì€ë¥ ì²ëŠ¬ë¥Œ ì¬ì©íììì€. ìŽê²ë€ì ë¹ì ì "unknowns"ì ëë€. ë€ížìí¬ê° íì ì¬ì© ê°ë¥íë€ê³ ìŠëª íêž° ìíŽ íì ìì€í ì ì¬ì©í ì ììµëë€.
Scenario Analysis: When to Use Which
ìëëŠ¬ì€ 1: API ìëµ íì± (ì€ë¥ ì²ëЬ)
íì¬ APIìì ì¬ì©ì ë°ìŽí°ë¥Œ ê°ì žì€ê³ ìë€ê³ ê°ì íŽ ë³Žê² ìµëë€. API ì€ëª ììë `status` íëê° ìë JSON ê°ì²Žê° ë°íëë€ê³ ëì ììµëë€. 컎íìŒ íìì ìŽë¥Œ ì 뢰í ì ììµëë€. ë€ížìí¬ê° ë€ìŽëê±°ë APIê° ë ìŽì ì¬ì©ëì§ ìì 500 ì€ë¥ë¥Œ ë°ííê±°ë ì못ë JSON 묞ììŽì ë°íí ì ììµëë€. ìŽê²ìŽ ì€ë¥ ì²ëЬì ììì ëë€.
async function fetchUser(userId: string): Promise<User> {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
// Handle HTTP errors (e.g., 404, 500)
throw new Error(`API Error: ${response.status}`);
}
const data = await response.json();
// Here you would also add runtime validation of the data structure
return data as User;
} catch (error) {
// Handle network errors, JSON parsing errors, etc.
console.error("Failed to fetch user:", error);
throw error; // Re-throw or handle gracefully
}
}
`never`륌 ì¬êž°ìì ì¬ì©íë ê²ì ë¶ì ì í©ëë€. ìëí멎 ì€íš ê°ë¥ì±ì 묎ííê³ íì ìì€í ìžë¶ì ìêž° ë묞ì ëë€.
ìëëŠ¬ì€ 2: UI 컎í¬ëíž ìí ë ëë§ (ìì í ê²ì¬)
ìŽì UI 컎í¬ëížê° ì¬ë¬ ì ì ìë ìí ì€ íëì ìì ì ìë€ê³ ê°ì íŽ ë³Žê² ìµëë€. ìŽ ìí륌 ì í늬ìŒìŽì ìœë ëŽìì ìì í ì ìŽí©ëë€. ìŽê²ì íë³ë ì ëìšê³Œ ìì í ê²ì¬ë¥Œ ìí ì벜í í볎ì ëë€.
type ComponentState =
| { status: 'loading' }
| { status: 'success'; data: string[] }
| { status: 'error'; message: string };
function renderComponent(state: ComponentState): string { // Returns an HTML string
switch (state.status) {
case 'loading':
return `<div>Loading...</div>`;
case 'success':
return `<ul>${state.data.map(item => `<li>${item}</li>`).join('')}</ul>`;
case 'error':
return `<div class="error">Error: ${state.message}</div>`;
default:
// If we later add a 'submitting' status, this line will protect us!
const _exhaustiveCheck: never = state;
throw new Error(`Unhandled state: ${_exhaustiveCheck}`);
}
}
ê°ë°ìê° ìë¡ìŽ ìí `{ status: 'idle' }`륌 ì¶ê°í멎 컎íìŒë¬ë ìŠì `renderComponent`륌 ë¶ìì íë€ê³ íìíì¬ ì»Ží¬ëížê° ë¹ ê³µê°ìŒë¡ ë ëë§ëë UI ë²ê·žë¥Œ ë°©ì§í©ëë€.
The Synergy: Combining Both Approaches for Robust Systems
ê°ì¥ íë ¥ì ìž ìì€í ì íë륌 ë€ë¥ž ê²ë³Žë€ ì ííì§ ììµëë€. ê·žë€ì 몚ë íšê» ì¬ì©í©ëë€. ì€ë¥ ì²ëЬë íŒëì€ë¬ìŽ ìžë¶ ìžê³ë¥Œ êŽëЬíë ë°ë©Ž ìì í ê²ì¬ë ëŽë¶ ë¡ì§ìŽ ê±Žì íê³ ìì íì§ íìží©ëë€. ì€ë¥ ì²ëЬ 겜ê³ì ì¶ë ¥ì ì¢ ì¢ ìì í ê²ì¬ì ì졎íë ìì€í ì ì ë ¥ìŽë©ëë€.
API ê°ì žì€êž° ìì 륌 ê°ì íŽ ë³Žê² ìµëë€. íšìë ììž¡í ì ìë ë€ížìí¬ ì€ë¥ë¥Œ ì²ëЬí ì ìì§ë§ ì ìŽë ë°©ììŒë¡ ì±ê³µíê±°ë ì€íší멎 ëëšžì§ ì í늬ìŒìŽì ìŽ ìì ê° ìê² ì²ëЬí ì ìë ììž¡ ê°ë¥íê³ ì íì íë 결곌륌 ë°íí©ëë€.
// 1. Define a predictable, well-typed result for our internal logic.
type FetchResult<T> =
| { status: 'success'; data: T }
| { status: 'error'; error: Error };
// 2. The function now uses Error Handling to produce a result that can be Exhaustively Checked.
async function fetchUserData(userId: string): Promise<FetchResult<User>> {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error(`API returned status ${response.status}`);
}
const data = await response.json();
// Add runtime validation here (e.g., with Zod or io-ts)
return { status: 'success', data: data as User };
} catch (error) {
// We catch ANY potential error and wrap it in our known structure.
return { status: 'error', error: error instanceof Error ? error : new Error('An unknown error occurred') };
}
}
// 3. The calling code can now use Exhaustive Checking for clean, safe logic.
async function displayUser(userId: string) {
const result = await fetchUserData(userId);
switch (result.status) {
case 'success':
console.log(`User name: ${result.data.name}`);
break;
case 'error':
console.error(`Failed to display user: ${result.error.message}`);
break;
default:
const _exhaustiveCheck: never = result;
// This ensures if we add a 'loading' status to FetchResult,
// this code block will fail to compile until we handle it.
return _exhaustiveCheck;
}
}
ìŽ ê²°í©ë íšíŽì 믿ì ì ìì ì ëë¡ ê°ë ¥í©ëë€. `fetchUserData` íšìë ê²œê³ ìí ìíì¬ ììž¡í ììë ë€ížìí¬ ìì²ì ìžê³ë¥Œ ììž¡ ê°ë¥í íë³ë ì ëìšìŒë¡ ë³íí©ëë€. ê·žë° ë€ì ì í늬ìŒìŽì ì ëëšžì§ë 컎íìŒ íì ìì ì± ê²ì¬ì ìì í ìì ë§ì íµíŽ ìŽ ê¹šëí ë°ìŽí° 구조ìì ìëí ì ììµëë€.
A Global Perspective: `never` in Other Languages
ë°ë¥ íì ë° ì»ŽíìŒ íì ìì ì±ì ê°ë ì TypeScriptìë§ ê³ ì íì§ ììµëë€. ê·žê²ì ë§ì íëì ìŽê³ ìì ì€ì¬ì ìž ìžìŽì í¹ì§ì ëë€. ë€ë¥ž ê³³ìì 구íëë ë°©ìì 볎멎 ìíížìšìŽ ìì§ëìŽë§ìì 귌볞ì ìž ì€ìì±ìŽ ê°íë©ëë€.
- Rust: Rustìë "never íì "ìŽëŒê³ íë `!` íì ìŽ ììµëë€. íì¬ ì€í ì€ë ë륌 ì¢ ë£íë `panic!()` ë§€í¬ë¡ì ê°ìŽ "diverge"íë íšìì ë°í íì ì ëë€. Rustì ê°ë ¥í `match` ííì (ì€ìì¹ ë²ì )ì Ʞ볞ì ìŒë¡ ìì ì±ì ìíí©ëë€. `enum`ìì `match`íê³ ëªšë ë³íì ë€ë£šì§ 못í멎 ìœëê° ì»ŽíìŒëì§ ììµëë€. ìžìŽê° Ʞ볞ì ìŒë¡ ìŽ ìì ì ì ê³µíêž° ë묞ì ìë `never` ížëŠìŽ íìíì§ ììµëë€.
- Swift: Swiftìë `Never`ëŒë ë¹ ìŽê±°íìŽ ììµëë€. ì€ë¥ë¥Œ ë°ì ìí€ê±°ë ì¢ ë£íì§ ìì íšì ëë ë©ìëê° ì ë ë°ííì§ ììì ëíëŽë ë° ì¬ì©ë©ëë€. Rustì ë§ì°¬ê°ì§ë¡ Swiftì `switch` 묞ì Ʞ볞ì ìŒë¡ ìì íŽìŒíë¯ë¡ ìŽê±°íìŒë¡ ìì í ë 컎íìŒ íì ìì ì±ì ì ê³µí©ëë€.
- Kotlin: Kotlinìë íì ìì€í ì ë°ë¥ íì ìž `Nothing` íì ìŽ ììµëë€. ì€ë¥ë¥Œ íì ë°ììí€ë íì€ ëŒìŽëžë¬ëЬì `TODO()` íšìì ê°ìŽ íšìê° ì ë ë°ííì§ ììì ëíëŽë ë° ì¬ì©ë©ëë€. Kotlinì `when` ííì (ì€ìì¹ì ëìŒ)ì ìì í ê²ì¬ìë ì¬ì©í ì ììŒë©° 컎íìŒë¬ë ííììŒë¡ ì¬ì©ë ë ìì íì§ ììŒë©Ž ê²œê³ ëë ì€ë¥ë¥Œ ë°íí©ëë€.
- Python (Type Hints í¬íš): Pythonì `typing` 몚ëìë ì ë ë°ííì§ ìë íšì륌 죌ì ì²ëЬíë ë° ì¬ì©í ììë `NoReturn`ìŽ í¬íšëìŽ ììµëë€. Pythonì íì ìì€í ì ì ì§ì ìŽê³ Rust ëë Swiftë§íŒ ì격íì§ ìì§ë§ ìŽë¬í 죌ìì Mypyì ê°ì ì ì ë¶ì ë구ì ê·ì€í ì 볎륌 ì ê³µíì¬ë³Žë€ ì² ì í ê²ì¬ë¥Œ ìí í ì ììµëë€.
ìŽë¬í ë€ìí ìíê³ìì ê³µíµì ìž ì€ë ëë íì ë 벚ìì ë¶ê°ë¥í ìí륌 ííí ì ìëë¡ ë§ëë ê²ìŽ ì 첎 ë²ê·ž íŽëì€ë¥Œ ì ê±°íë ê°ë ¥í ë°©ë²ìŽëŒë ìžìì ëë€.
Actionable Insights and Best Practices
ìŽ ê°ë ¥í ê°ë ì ìŒì ì 묎ì íµí©íë €ë©Ž ë€ì ë°©ë²ì ê³ ë €íììì€.
- íë³ë ì ëìšì ìì©íììì€: ì¬ë¬ ê°ì ê°ë³ ë³íìŽ ë ì ìë íì ìŽ ìì ëë§ë€ íë³ë ì ëìš (íê·žê° ì§ì ë ì ëìš ëë í© íì ìŽëŒê³ ë íš)ìŒë¡ ë°ìŽí°ë¥Œ ì ê·¹ì ìŒë¡ 몚ëžë§íììì€. ìŽê²ì ìì í ê²ì¬ê° 구ì¶ëë êž°ìŽì ëë€. API 결곌, 컎í¬ëíž ìí ë° ìŽë²€ížë¥Œ ìŽë¬í ë°©ììŒë¡ 몚ëžë§íììì€.
- ë¶ë² ìí륌 ííí ì ìëë¡ ë§ëììì€: ìŽê²ì íì êž°ë° ì€ê³ì íµì¬ ìì¹ì ëë€. ì¬ì©ìê° êŽëЬììŽì ê²ì€ížê° ëìì ë ììë ê²œì° íì ìì€í ììŽë¥Œ ë°ìíŽìŒí©ëë€. ì¬ë¬ ê°ì ì íì ë¶ìž íëê·ž (isAdmin?: boolean; isGuest?: boolean;) ëì ì ëìš (A | B)ì ì¬ì©íììì€. `never` íì ì ìíê° ííí ì ììì ìŠëª íë ê¶ê·¹ì ìž ë구ì ëë€.
-
ì¬ì¬ì© ê°ë¥í ëì°ë¯ž íšì륌 ë§ëììì€: `default` ìŒìŽì€ë ê°ëší ëì°ë¯ž íšìë¡ ë ê¹ëíê² ë§ë€ ì ììµëë€. ìŽë ìœëê° ë°íìì ëë¬í멎 (ë¶ê°ë¥íŽìŒ íš) ë ì€ëª
ì ìž ì€ë¥ë¥Œ ì ê³µí©ëë€.
function assertNever(value: never): never { throw new Error(`Unhandled discriminated union member: ${JSON.stringify(value)}`); } // Usage: default: assertNever(shape); // Cleaner and provides a better runtime error message. - 컎íìŒë¬ì ë§ì ë€ìŒììì€: ìì ì± ì€ë¥ë¥Œ ê·ì°®ì ê²ìŒë¡ ì¬êž°ì§ ë§ê³ ì ë¬Œë¡ ì¬êž°ììì€. 컎íìŒë¬ë íë¡ê·žëšìì ë ŒëŠ¬ì ê²°íšì ë°ê²¬í ë¶ì§ë°íê³ ìëíë ìœë ê²í ì ìí ì íê³ ììµëë€. ê°ì¬íê³ ìœë륌 ìì íììì€.
Conclusion: The Silent Guardian of Your Codebase
`never` íì ì ìŽë¡ ì ížêž°ì¬ ê·ž ìŽìì ëë€. ê·žê²ì ê°ë ¥íê³ ìêž° 묞ìíëê³ ì ì§ êŽëЬ ê°ë¥í ìíížìšìŽë¥Œ 구ì¶íêž°ìí ì€ì©ì ìŽê³ ê°ë ¥í ë구ì ëë€. ìì í ê²ì¬ë¥Œ ìíŽ íì©íšìŒë¡ìš ì°ëЬë 귌볞ì ìŒë¡ ì íì±ì ì ê·Œíë ë°©ìì ë°ê¿ëë€. ì°ëЬë ë ŒëŠ¬ì ìì ì±ì 볎ì¥íë ë¶ëŽì ì€ë¥ê° ë°ìíêž° ì¬ìŽ ìžê°ì êž°ìµê³Œ ë°íì í ì€ížìì ì€ë¥ê° ìê³ ìëíë 컎íìŒ íì íì ë¶ìì ìžê³ë¡ ì®ê¹ëë€.
ì íµì ìž ì€ë¥ ì²ëЬë ìžë¶ ìì€í ì ììž¡í ì ìë í¹ì±ì êŽëЬíë ë° íìì ìŽì§ë§ ìì í ê²ì¬ë ì í늬ìŒìŽì ì ëŽë¶ì ìŽê³ ìë €ì§ ë¡ì§ì ëí 볎ìì 볎ìŠì ì ê³µí©ëë€. íšê» ê·žë€ì ë²ê·žì ëí ê³ìžµí ë ë°©ìŽë¥Œ íì±íì¬ ì€íšíêž° ì¬ìžë¿ë§ ìëëŒ ì¶ë¡ íêž° ìœê³ 늬í©í°ë§íêž°ì ë ìì í ìì€í ì ë§ëëë€.
ë€ìì `switch` 묞ìŽë ìë €ì§ ê°ë¥ì± ìžížì ëí ꞎ `if-else-if` 첎ìžì ìì±íë ìì ì ë°ê²¬í멎 ì ì ë©ì¶ê³ `never` íì ìŽ ìŽ ìœëì ëí ì¡°ì©í 볎ížì ìí ì í ì ìëì§ ë¬ŒìŽë³Žììì€. ê·žë ê² íšìŒë¡ìš ë¹ì ì ì€ëë ì³ìë¿ë§ ìëëŒ ëŽìŒì ê°ê³Œì ëíŽìë ê°íëë ìœë륌 ìì±íê² ë ê²ì ëë€.