TypeScriptì 'infer' í€ìëì ëí ì¬ìžµ ë¶ììŒë¡, ê°ë ¥í íì ì¡°ì ë° í¥ìë ìœë ëª íì±ì ìíŽ ì¡°ê±Žë¶ íì ìì ê³ êž ì¬ì©ë²ì ìŽíŽëŽ ëë€.
ì¡°ê±Žë¶ íì ì¶ë¡ : TypeScriptì 'infer' í€ìë ë§ì€í°íêž°
TypeScriptì íì
ìì€í
ì ê°ë ¥íê³ ì ì§ êŽëЬ ê°ë¥í ìœë륌 ë§ë€êž° ìí ê°ë ¥í ë구륌 ì ê³µí©ëë€. ìŽë¬í ë구 ì€ìì ì¡°ê±Žë¶ íì
ì ë³µì¡í íì
êŽê³ë¥Œ íííë ë€ì¬ë€ë¥í ë©ì»€ëìŠìŒë¡ ëëë¬ì§ëë€. í¹í infer í€ìëë ì¡°ê±Žë¶ íì
ëŽìì ê³ êž ê°ë¥ì±ì ìŽìŽì£ŒìŽ ì êµí íì
ì¶ì¶ ë° ì¡°ìì ê°ë¥íê² í©ëë€. ìŽ í¬êŽì ìž ê°ìŽëììë inferì ë³µì¡ì±ì ìŽíŽë³Žê³ ì€ì ìì ì íµì°°ë ¥ì ì ê³µíì¬ ì¬ì©ë²ì ë§ì€í°íëë¡ ëìµëë€.
ì¡°ê±Žë¶ íì ìŽíŽ
infer륌 ìŽíŽë³Žêž° ì ì ì¡°ê±Žë¶ íì
ì Ʞ볞 ì¬íì íì
íë ê²ìŽ ì€ìí©ëë€. ì¡°ê±Žë¶ íì
ì ì¬ì©í멎 JavaScriptì ìŒí ì°ì°ìì ì ì¬íê² ì¡°ê±Žì ë°ëŒ ë¬ëŒì§ë íì
ì ì ìí ì ììµëë€. 구묞ì ë€ì íšíŽì ë°ëŠ
ëë€.
T extends U ? X : Y
ì¬êž°ì íì
T륌 íì
Uì í ë¹í ì ìë ê²œì° ê²°ê³Œ íì
ì XìŽê³ , ê·žë ì§ ììŒë©Ž Yì
ëë€.
ìì :
type IsString<T> = T extends string ? true : false;
type StringCheck = IsString<string>; // type StringCheck = true
type NumberCheck = IsString<number>; // type NumberCheck = false
ìŽ ê°ëší ìì ììë ì¡°ê±Žë¶ íì
ì ì¬ì©íì¬ íì
ìŽ ë¬žììŽìžì§ ì¬ë¶ë¥Œ íìžíë ë°©ë²ì 볎ì¬ì€ëë€. ìŽ ê°ë
ì ë ë³µì¡í ìë늬ì€ë¡ íì¥ëìŽ infer í€ìë륌 ìí êžžì ìŽìŽì€ëë€.
'infer' í€ìë ìê°
infer í€ìëë ì¡°ê±Žë¶ íì
ì true ë¶êž° ëŽìì ê²ì¬ ì€ìž íì
ìì ì¶ë¡ í ì ìë íì
ë³ì륌 ëì
íë ë° ì¬ì©ë©ëë€. ìŽë¥Œ íµíŽ íì
ì í¹ì ë¶ë¶ì ì¶ì¶íê³ ê²°ê³Œ íì
ìì ì¬ì©í ì ììµëë€.
구묞:
T extends (infer R) ? X : Y
ìŽ êµ¬ë¬žìì Rì Tì 구조ìì ì¶ë¡ ëë íì
ë³ìì
ëë€. Tê° íšíŽê³Œ ìŒì¹í멎 Rì ì¶ë¡ ë íì
ì 볎ì íê³ ê²°ê³Œ íì
ì XìŽê³ , ê·žë ì§ ììŒë©Ž Yì
ëë€.
'infer' ì¬ì©ë²ì Ʞ볞 ìì
1. íšìì ë°í íì ì¶ë¡
ìŒë°ì ìž ì¬ì© ì¬ë¡ë íšìì ë°í íì ì ì¶ë¡ íë ê²ì ëë€. ìŽë ë€ì ì¡°ê±Žë¶ íì ìŒë¡ ë¬ì±í ì ììµëë€.
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
ì€ëª :
T extends (...args: any) => any: ìŽ ì ìœ ì¡°ê±ŽìTê° íšììžì§ íìží©ëë€.(...args: any) => infer R: ìŽ íšíŽì íšìì ìŒì¹íê³ ë°í íì ìRë¡ ì¶ë¡ í©ëë€.R : any:Tê° íšìê° ìë멎 결곌 íì ìanyì ëë€.
ìì :
function greet(name: string): string {
return `Hello, ${name}!`;
}
type GreetingReturnType = ReturnType<typeof greet>; // type GreetingReturnType = string
function calculate(a: number, b: number): number {
return a + b;
}
type CalculateReturnType = ReturnType<typeof calculate>; // type CalculateReturnType = number
ìŽ ìì ììë ReturnTypeìŽ greet ë° calculate íšìì ë°í íì
ì ì±ê³µì ìŒë¡ ì¶ì¶íë ë°©ë²ì 볎ì¬ì€ëë€.
2. ë°°ìŽ ìì íì ì¶ë¡
ë ë€ë¥ž ë¹ë²í ì¬ì© ì¬ë¡ë ë°°ìŽì ìì íì ì ì¶ì¶íë ê²ì ëë€.
type ElementType<T> = T extends (infer U)[] ? U : never;
ì€ëª :
T extends (infer U)[]: ìŽ íšíŽì ë°°ìŽê³Œ ìŒì¹íê³ ìì íì ìUë¡ ì¶ë¡ í©ëë€.U : never:Tê° ë°°ìŽìŽ ìë멎 결곌 íì ìneverì ëë€.
ìì :
type StringArrayElement = ElementType<string[]>; // type StringArrayElement = string
type NumberArrayElement = ElementType<number[]>; // type NumberArrayElement = number
type MixedArrayElement = ElementType<(string | number)[]>; // type MixedArrayElement = string | number
type NotAnArray = ElementType<number>; // type NotAnArray = never
ìŽê²ì ElementTypeìŽ ë€ìí ë°°ìŽ íì
ì ìì íì
ì ì¬ë°ë¥Žê² ì¶ë¡ íë ë°©ë²ì 볎ì¬ì€ëë€.
ê³ êž 'infer' ì¬ì©ë²
1. íšìì ë§€ê°ë³ì ì¶ë¡
ë°í íì
ì ì¶ë¡ íë ê²ê³Œ ì ì¬íê² infer ë° ííì ì¬ì©íì¬ íšìì ë§€ê°ë³ì륌 ì¶ë¡ í ì ììµëë€.
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
ì€ëª :
T extends (...args: any) => any: ìŽ ì ìœ ì¡°ê±ŽìTê° íšììžì§ íìží©ëë€.(...args: infer P) => any: ìŽ íšíŽì íšìì ìŒì¹íê³ ë§€ê°ë³ì íì ì ííPë¡ ì¶ë¡ í©ëë€.P : never:Tê° íšìê° ìë멎 결곌 íì ìneverì ëë€.
ìì :
function logMessage(message: string, level: 'info' | 'warn' | 'error'): void {
console.log(`[${level.toUpperCase()}] ${message}`);
}
type LogMessageParams = Parameters<typeof logMessage>; // type LogMessageParams = [message: string, level: "info" | "warn" | "error"]
function processData(data: any[], callback: (item: any) => void): void {
data.forEach(callback);
}
type ProcessDataParams = Parameters<typeof processData>; // type ProcessDataParams = [data: any[], callback: (item: any) => void]
Parametersë ë§€ê°ë³ì íì
ì ííë¡ ì¶ì¶íì¬ íšìì ìžìì ììì íì
ì ì ì§í©ëë€.
2. ê°ì²Ž íì ìì ìì± ì¶ì¶
infer륌 ì¬ì©íì¬ ê°ì²Ž íì
ìì í¹ì ìì±ì ì¶ì¶í ìë ììµëë€. ìŽë¥Œ ìíŽìë ë ë³µì¡í ì¡°ê±Žë¶ íì
ìŽ íìíì§ë§ ê°ë ¥í íì
ì¡°ììŽ ê°ë¥í©ëë€.
type PickByType<T, U> = {
[K in keyof T as T[K] extends U ? K : never]: T[K];
};
ì€ëª :
K in keyof T: ìŽê²ì íìTì 몚ë í€ë¥Œ ë°ë³µí©ëë€.T[K] extends U ? K : never: ìŽ ì¡°ê±Žë¶ íì ì í€Kì ìì± íì (ìŠ,T[K])ì íìUì í ë¹í ì ìëì§ íìží©ëë€. ê·žë ë€ë©Ž í€Kê° ê²°ê³Œ íì ì í¬íšëê³ , ê·žë ì§ ììŒë©Žnever륌 ì¬ì©íì¬ ì ìžë©ëë€.- ì 첎 구조ë íì
ìŽ
U륌 íì¥íë ìì±ë§ ìë ì ê°ì²Ž íì ì ë§ëëë€.
ìì :
interface Person {
name: string;
age: number;
city: string;
country: string;
}
type StringProperties = PickByType<Person, string>; // type StringProperties = { name: string; city: string; country: string; }
type NumberProperties = PickByType<Person, number>; // type NumberProperties = { age: number; }
PickByType륌 ì¬ì©í멎 Ʞ졎 íì
ìì í¹ì íì
ì ìì±ë§ í¬íšíë ì íì
ì ë§ë€ ì ììµëë€.
3. ì€ì²©ë íì ì¶ë¡
infer륌 ì°ê²°íê³ ì€ì²©íì¬ ê¹ìŽ ì€ì²©ë 구조ìì íì
ì ì¶ì¶í ì ììµëë€. ì륌 ë€ìŽ ì€ì²©ë ë°°ìŽì ê°ì¥ ì쪜 ììì íì
ì ì¶ì¶íë ê²ì ê³ ë €íŽë³Žìžì.
type DeepArrayElement<T> = T extends (infer U)[] ? DeepArrayElement<U> : T;
ì€ëª :
T extends (infer U)[]: ìŽê²ìTê° ë°°ìŽìžì§ íìžíê³ ìì íì ìUë¡ ì¶ë¡ í©ëë€.DeepArrayElement<U>:Tê° ë°°ìŽìŽë©Ž íì ì ìì íìUë¡DeepArrayElement륌 ì¬ê·ì ìŒë¡ ížì¶í©ëë€.T:Tê° ë°°ìŽìŽ ìë멎 íì ìTì첎륌 ë°íí©ëë€.
ìì :
type NestedStringArray = string[][][];
type DeepString = DeepArrayElement<NestedStringArray>; // type DeepString = string
type MixedNestedArray = (number | string)[][][][];
type DeepMixed = DeepArrayElement<MixedNestedArray>; // type DeepMixed = string | number
type RegularNumber = DeepArrayElement<number>; // type RegularNumber = number
ìŽ ì¬ê·ì ì ê·Œ ë°©ìì ì¬ì©í멎 ë°°ìŽìì ê°ì¥ ê¹ì ì€ì²© ìì€ì ìë ììì íì ì ì¶ì¶í ì ììµëë€.
ì€ì ìì© íë¡ê·žëš
infer í€ìëë ëì íì
ì¡°ììŽ íìí ë€ìí ìë늬ì€ìì ìì© íë¡ê·žëšì ì°Ÿìµëë€. ë€ìì ëª ê°ì§ ì€ì ìì
ëë€.
1. íì ìì ìŽë²€íž ìŽë¯ží° ìì±
infer륌 ì¬ì©íì¬ ìŽë²€íž ížë€ë¬ê° ì¬ë°ë¥ž ë°ìŽí° íì
ì ìì íëë¡ ë³Žì¥íë íì
ìì ìŽë²€íž ìŽë¯ží°ë¥Œ ë§ë€ ì ììµëë€.
type EventMap = {
'data': { value: string };
'error': { message: string };
};
type EventName<T extends EventMap> = keyof T;
type EventData<T extends EventMap, K extends EventName<T>> = T[K];
type EventHandler<T extends EventMap, K extends EventName<T>> = (data: EventData<T, K>) => void;
class EventEmitter<T extends EventMap> {
private listeners: { [K in EventName<T>]?: EventHandler<T, K>[] } = {};
on<K extends EventName<T>>(event: K, handler: EventHandler<T, K>): void {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event]!.push(handler);
}
emit<K extends EventName<T>>(event: K, data: EventData<T, K>): void {
this.listeners[event]?.forEach(handler => handler(data));
}
}
const emitter = new EventEmitter<EventMap>();
emitter.on('data', (data) => {
console.log(`Received data: ${data.value}`);
});
emitter.on('error', (error) => {
console.error(`An error occurred: ${error.message}`);
});
emitter.emit('data', { value: 'Hello, world!' });
emitter.emit('error', { message: 'Something went wrong.' });
ìŽ ìì ìì EventDataë ì¡°ê±Žë¶ íì
곌 infer륌 ì¬ì©íì¬ í¹ì ìŽë²€íž ìŽëŠê³Œ ì°ê²°ë ë°ìŽí° íì
ì ì¶ì¶íì¬ ìŽë²€íž ížë€ë¬ê° ì¬ë°ë¥ž íì
ì ë°ìŽí°ë¥Œ ìì íëë¡ ë³Žì¥í©ëë€.
2. íì ìì 늬ëì 구í
infer륌 íì©íì¬ ìí êŽëŠ¬ë¥Œ ìí íì
ìì 늬ëì íšì륌 ë§ë€ ì ììµëë€.
type Action<T extends string, P = undefined> = P extends undefined
? { type: T }
: { type: T; payload: P };
type Reducer<S, A extends Action<string>> = (state: S, action: A) => S;
// Example Actions
type IncrementAction = Action<'INCREMENT'>;
type DecrementAction = Action<'DECREMENT'>;
type SetValueAction = Action<'SET_VALUE', number>;
// Example State
interface CounterState {
value: number;
}
// Example Reducer
const counterReducer: Reducer<CounterState, IncrementAction | DecrementAction | SetValueAction> = (
state: CounterState,
action: IncrementAction | DecrementAction | SetValueAction
): CounterState => {
switch (action.type) {
case 'INCREMENT':
return { ...state, value: state.value + 1 };
case 'DECREMENT':
return { ...state, value: state.value - 1 };
case 'SET_VALUE':
return { ...state, value: action.payload };
default:
return state;
}
};
// Usage
const initialState: CounterState = { value: 0 };
const newState1 = counterReducer(initialState, { type: 'INCREMENT' }); // newState1.value is 1
const newState2 = counterReducer(newState1, { type: 'SET_VALUE', payload: 10 }); // newState2.value is 10
ìŽ ìì ë `infer`륌 ì§ì ì¬ì©íì§ ìì§ë§ ë ë³µì¡í 늬ëì ìë늬ì€ë¥Œ ìí í ë륌 ë§ë ší©ëë€. `infer`ë ë€ìí `Action` íì ìì `payload` íì ì ëì ìŒë¡ ì¶ì¶íë ë° ì ì©íì¬ ëŠ¬ëì íšì ëŽìì ë ì격í íì ê²ì¬ë¥Œ íì©í ì ììµëë€. ìŽë ìë§ì ì¡ì 곌 ë³µì¡í ìí 구조륌 ê°ì§ ëê·ëªš ì í늬ìŒìŽì ìì í¹í ì ì©í©ëë€.
3. API ìëµìì ëì íì ìì±
APIë¡ ìì
í ë infer륌 ì¬ì©íì¬ API ìëµ êµ¬ì¡°ìì TypeScript íì
ì ìëìŒë¡ ìì±í ì ììµëë€. ìŽë ìžë¶ ë°ìŽí° ìì€ì ìíž ìì©í ë íì
ìì ì±ì 볎ì¥íë ë° ëììŽ ë©ëë€.
ìŒë° API ìëµìì ë°ìŽí° íì ì ì¶ì¶íë €ë ëšìíë ìë늬ì€ë¥Œ ê³ ë €íììì€.
type ApiResponse<T> = {
status: number;
data: T;
message?: string;
};
type ExtractDataType<T> = T extends ApiResponse<infer U> ? U : never;
// Example API Response
type User = {
id: number;
name: string;
email: string;
};
type UserApiResponse = ApiResponse<User>;
type ExtractedUser = ExtractDataType<UserApiResponse>; // type ExtractedUser = User
ExtractDataTypeë infer륌 ì¬ì©íì¬ ApiResponse<U>ìì íì
U륌 ì¶ì¶íì¬ APIìì ë°íë ë°ìŽí° 구조ì ìì í íì
ë°©ììŒë¡ ì¡ìžì€í ì ìëë¡ í©ëë€.
ëªšë² ì¬ë¡ ë° ê³ ë € ì¬í
- ëª
íì± ë° ê°ë
ì±: ìœë ê°ë
ì±ì ëìŽêž° ìíŽ ì€ëª
ì ìž íì
ë³ì ìŽëŠ(ì: ê·žë¥
RëìReturnType)ì ì¬ì©í©ëë€. - ì±ë¥:
inferë ê°ë ¥íì§ë§ 곌ëíê² ì¬ì©í멎 íì ê²ì¬ ì±ë¥ì ìí¥ì ì€ ì ììµëë€. í¹í ëê·ëªš ìœëë² ìŽì€ììë ì ì€íê² ì¬ì©íììì€. - ì€ë¥ ì²ëЬ: íì
ìŽ ìì íšíŽê³Œ ìŒì¹íì§ ìë 겜ì°ë¥Œ ì²ëЬíêž° ìíŽ ì¡°ê±Žë¶ íì
ì
falseë¶êž°ì íì íŽë°± íì (ì:anyëënever)ì ì ê³µí©ëë€. - ë³µì¡ì±: ì€ì²©ë
inferë¬žìŽ ìë ì§ëì¹ê² ë³µì¡í ì¡°ê±Žë¶ íì ì ìŽíŽíê³ ì ì§ êŽëЬíêž° ìŽë €ìì§ ì ììŒë¯ë¡ íŒíììì€. íìí ê²œì° ìœë륌 ë ìê³ êŽëЬíêž° ì¬ìŽ íì ìŒë¡ 늬í©í°ë§íììì€. - í ì€íž: ë€ìí ì ë ¥ íì ìŒë¡ ì¡°ê±Žë¶ íì ì ì² ì í í ì€ížíì¬ ììëë¡ ìëíëì§ íìží©ëë€.
êžë¡ë² ê³ ë € ì¬í
êžë¡ë² 컚í
ì€ížìì TypeScriptì infer륌 ì¬ì©í ë ë€ì ì¬íì ê³ ë €íììì€.
- ì§ìí ë° êµì í(i18n): íì ì ë€ë¥ž ë¡ìº ë° ë°ìŽí° íìì ì ìíŽìŒ í ì ììµëë€. ì¡°ê±Žë¶ íì 곌 `infer`륌 ì¬ì©íì¬ ë¡ìºë³ ì구 ì¬íì ë°ëŒ ë€ìí ë°ìŽí° 구조륌 ëì ìŒë¡ ì²ëЬí©ëë€. ì륌 ë€ìŽ ë ì§ì íµíë êµê°ë§ë€ ë€ë¥Žê² ííë ì ììµëë€.
- êžë¡ë² ì²ì€ì ìí API ëììž: ì ìžê³ì ìŒë¡ ì ê·Œì±ì ê³ ë €íì¬ API륌 ëììžíììì€. ì¬ì©ìì ìì¹ì êŽê³ììŽ ìŽíŽíê³ ì²ëЬíêž° ì¬ìŽ ìŒêŽë ë°ìŽí° 구조ì íìì ì¬ì©íììì€. íì ì ìë ìŽë¬í ìŒêŽì±ì ë°ìíŽìŒ í©ëë€.
- ìê°ë: ë ì§ì ìê°ì ì²ëЬí ë ìê°ë ì°šìŽì ì ìíììì€. ì ì í ëŒìŽëžë¬ëЬ(ì: Luxon, date-fns)륌 ì¬ì©íì¬ ìê°ë ë³íì ì²ëЬíê³ ë€ìí ì§ììì ì íí ë°ìŽí° ííì 볎ì¥í©ëë€. API ìëµìì ë ì§ì ìê°ì UTC íììŒë¡ íííë ê²ì ê³ ë €íììì€.
- 묞íì ì°šìŽ: ë°ìŽí° íí곌 íŽìì 묞íì ì°šìŽë¥Œ ìžìíììì€. ì륌 ë€ìŽ ìŽëŠ, 죌ì ë° ì í ë²ížë êµê°ë§ë€ ë€ë¥ž íìì ê°ì§ ì ììµëë€. íì ì ìê° ìŽë¬í ë³íì ìì©í ì ìëì§ íìžíììì€.
- íµí ì²ëЬ: íµí ê°ì ì²ëЬí ë ìŒêŽë íµí íí(ì: ISO 4217 íµí ìœë)ì ì¬ì©íê³ íµí ë³íì ì ì íê² ì²ëЬí©ëë€. ì ë°ë 묞ì 륌 íŒíê³ ì íí ê³ì°ì 볎ì¥íêž° ìíŽ íµí ì¡°ìì ìíŽ ì€ê³ë ëŒìŽëžë¬ëŠ¬ë¥Œ ì¬ì©íììì€.
ì륌 ë€ìŽ ë€ë¥ž ì§ììì ì¬ì©ì íë¡íì ê°ì žì€ê³ 죌ì íììŽ êµê°ì ë°ëŒ ë€ë¥ž ìë늬ì€ë¥Œ ê³ ë €íììì€. ì¡°ê±Žë¶ íì 곌 `infer`륌 ì¬ì©íì¬ ì¬ì©ì ìì¹ì ë°ëŒ íì ì ì륌 ëì ìŒë¡ ì¡°ì í ì ììµëë€.
type AddressFormat<CountryCode extends string> = CountryCode extends 'US'
? { street: string; city: string; state: string; zipCode: string; }
: CountryCode extends 'CA'
? { street: string; city: string; province: string; postalCode: string; }
: { addressLines: string[]; city: string; country: string; };
type UserProfile<CountryCode extends string> = {
id: number;
name: string;
email: string;
address: AddressFormat<CountryCode>;
countryCode: CountryCode; // Add country code to profile
};
// Example Usage
type USUserProfile = UserProfile<'US'>; // Has US address format
type CAUserProfile = UserProfile<'CA'>; // Has Canadian address format
type GenericUserProfile = UserProfile<'DE'>; // Has Generic (international) address format
`UserProfile` íì ì `countryCode`륌 í¬íšíê³ ìŽ ìœë륌 êž°ë°ìŒë¡ ì¡°ê±Žë¶ íì ì ì¬ì©í멎 ê° ì§ìì ëíŽ ììëë íì곌 ìŒì¹íëë¡ `address` íì ì ëì ìŒë¡ ì¡°ì í ì ììµëë€. ìŽë¥Œ íµíŽ ë€ìí êµê°ìì ë€ìí ë°ìŽí° íìì ìì í íì ë°©ììŒë¡ ì²ëЬí ì ììµëë€.
ê²°ë¡
infer í€ìëë TypeScriptì íì
ìì€í
ì ê°ë ¥í ì¶ê° êž°ë¥ìŒë¡, ì¡°ê±Žë¶ íì
ëŽìì ì êµí íì
ì¡°ì ë° ì¶ì¶ì ê°ë¥íê² í©ëë€. infer륌 ë§ì€í°í멎 ë ê°ë ¥íê³ íì
ìì íë©° ì ì§ êŽëЬ ê°ë¥í ìœë륌 ë§ë€ ì ììµëë€. íšì ë°í íì
ì¶ë¡ ë¶í° ë³µì¡í ê°ì²Žìì ìì± ì¶ì¶ì ìŽë¥Žêž°ê¹ì§ ê°ë¥ì±ì ë°©ëí©ëë€. ìœëê° ì¥êž°ì ìŒë¡ ìŽíŽíê³ ì ì§ êŽëЬí ì ìëë¡ infer륌 ì ì€íê² ì¬ì©íê³ ëª
íì±ê³Œ ê°ë
ì±ì ì°ì ìíììì€.
ìŽ ê°ìŽëììë inferì ìì© íë¡ê·žëšì ëí í¬êŽì ìž ê°ì륌 ì ê³µíìµëë€. ì ê³µë ìì 륌 ì€ííê³ ì¶ê° ì¬ì© ì¬ë¡ë¥Œ íìíê³ infer륌 íì©íì¬ TypeScript ê°ë° ìí¬íë¡ë¥Œ í¥ììí€ììì€.