π TypeScript `any` νμ λ¨μ©: νμ μ€ν¬λ¦½νΈλ₯Ό μ°μ§ μλ κ²κ³Ό λ€λ¦μλ μ½λ
Summary
any
νμ
μ λ¨κΈ°μ μΈ νΈμλ₯Ό μ 곡νμ§λ§, κ²°κ΅ νμ
μ€ν¬λ¦½νΈ μ¬μ©μ μλ―Έλ₯Ό ν΄μμν€κ³ μ μ¬μ μΈ λ²κ·Έλ₯Ό μ λ°νλ©° μ½λ νμ§μ λ¨μ΄λ¨λ¦½λλ€. μ κ·Ήμ μΈ νμ
μΆλ‘ κ³Ό λͺ
μμ μΈ νμ
μ μ, κ·Έλ¦¬κ³ μ λ€λ¦ λ° unknown
νμ
μ ν΅ν΄ νμ
μμ μ±μ ν보ν΄μΌ ν©λλ€.
Why Wrong?
TypeScriptμ any
νμ
μ λͺ¨λ μ’
λ₯μ κ°μ νμ©νμ¬ μΌμμ μΌλ‘ νΈλ¦¬ν΄ λ³΄μΌ μ μμ΅λλ€. νμ§λ§ μ΄λ νμ
μμ€ν
μ ν΅μ¬ μ΄μ μΈ 'νμ
μμ μ±'μ μμ ν μμ€νκ² λ§λλλ€. any
νμ
μ μ¬μ©νλ©΄ TypeScript μ»΄νμΌλ¬κ° ν΄λΉ λ³μμ νμ
μ μ ν κ²μ¬νμ§ μμΌλ―λ‘, μ»΄νμΌ μμ μ λ°κ²¬ν μ μμλ μ μ¬μ μΈ λ²κ·Έλ€μ΄ λ°νμ μλ¬λ‘ μ΄μ΄μ§ κ°λ₯μ±μ΄ κ·Ήλλ‘ λμμ§λλ€. νΉν μΈλΆ API μλ΅μ΄λ 볡μ‘ν κ°μ²΄λ₯Ό any
λ‘ μ²λ¦¬νλ©΄, λ°μ΄ν° ꡬ쑰λ₯Ό μμΈ‘νκΈ° μ΄λ €μμ Έ μ½λμ κ°λ
μ±κ³Ό μ μ§λ³΄μμ±μ΄ ν¬κ² μ ν΄λ©λλ€. μ΄λ 리ν©ν λ§μ΄λ νμ
μ μ€λ₯ λ°μ κ°λ₯μ±μ λμ΄λ©°, IDEμ μλ μμ±(IntelliSense) κΈ°λ₯ νμ©λ λΆκ°λ₯νκ² λ§λ€μ΄ κ°λ° μμ°μ±μ λ¨μ΄λ¨λ¦½λλ€. any
νμ
μ νμ
μ€ν¬λ¦½νΈλ₯Ό μ¬μ©νλ©΄μλ κ·Έ μ΄μ μ ν¬κΈ°νλ κ²κ³Ό κ°μ νλ‘μ νΈμ κ²¬κ³ μ±μ ν΄μΉλ μ£Όλ²μ΄ λ©λλ€.
How to Fix?
νμ
μ€ν¬λ¦½νΈλ₯Ό μ¬μ©νλ λͺ©μ μ μΆ©μ€νκ², νμ λͺ
μμ μΈ νμ
μ μλ₯Ό μ°μ ν΄μΌ ν©λλ€. 볡μ‘νκ±°λ λΆνμ€ν λ°μ΄ν° ꡬ쑰μλ interface
λ type alias
λ₯Ό νμ©νμ¬ νμ
μ λͺ
ννκ² μ μΈν©λλ€. λ€μν νμ
μ μ μ°νκ² λμνλ©΄μλ νμ
μμ μ±μ μ μ§ν΄μΌ ν λλ **μ λ€λ¦(Generics)**μ μ κ·Ή νμ©νμ¬ μ¬μ¬μ© κ°λ₯νκ³ νμ
μμ ν μ½λλ₯Ό μμ±ν μ μμ΅λλ€. μ΄λ€ κ°μ΄ μ¬μ§ μ μ μμ§λ§ μ¬μ© μμ μ νμ
μ λͺ
νν νκ³ μΆλ€λ©΄ any
λμ unknown
νμ
μ μ¬μ©νλ κ²μ΄ μ’μ΅λλ€. unknown
μ any
μ²λΌ λͺ¨λ κ°μ λ°μ μ μμ§λ§, ν΄λΉ κ°μ μ¬μ©νκΈ° μ μ λ°λμ νμ
κ²μ¬(type narrowing)λ₯Ό ν΅ν΄ νμ
μ μ’νμΌ νλ―λ‘ λ°νμ μλ¬λ₯Ό λ°©μ§νλ λ° ν¨μ¬ ν¨κ³Όμ μ
λλ€. λ§μ§λ§μΌλ‘, νλ‘μ νΈ λ΄μμ any
μ¬μ©μ κ°μ νμ§ μκΈ° μν΄ @typescript-eslint/no-explicit-any
μ κ°μ ESLint κ·μΉμ μ€μ νμ¬ any
μ¬μ©μ κ²½κ³ νκ±°λ μμ μλ¬λ‘ μ²λ¦¬νλ κ²μ΄ λͺ¨λ² μ¬λ‘μ
λλ€.
Before Code (Bad)
// π¨ λ¬Έμ κ° λλ μ½λ: any νμ
λ¨μ©
// μΈλΆ APIμμ λ°μμ¨ λ°μ΄ν°λ₯Ό μ²λ¦¬νλ ν¨μ (λ°μ΄ν° κ΅¬μ‘°κ° νμ μΌμ νμ§ μλ€κ³ κ°μ )
// 'μΌλ¨' anyλ‘ λ°μμ νμ
κ²μ¬λ₯Ό ννΌν©λλ€.
function processUserData(data: any) {
console.log(`User ID: ${data.id}`);
console.log(`User Name: ${data.name}`);
// dataμ 'address' μμ±μ΄ μκ±°λ 'address'κ° κ°μ²΄κ° μλλ©΄ λ°νμ μλ¬ λ°μ κ°λ₯μ±!
console.log(`User Address: ${data.address.street}`);
if (data.status === 'active') {
// dataμ 'lastLogin'μ΄ μκ±°λ μ«μκ° μλλ©΄ Date κ°μ²΄ μμ± λ° toLocaleStringμμ λ°νμ μλ¬ λ°μ κ°λ₯μ±!
console.log(`Last login: ${new Date(data.lastLogin).toLocaleString()}`);
}
return data.id;
}
// μ ν¨ν λ°μ΄ν° (μ΄ κ²½μ°λ λ¬Έμ κ° μμ΄ λ³΄μ
λλ€)
processUserData({
id: '123',
name: 'Alice',
status: 'active',
lastLogin: 1678886400000,
address: { street: 'Main St', city: 'Anytown' }
});
// β¨ νμ§λ§, νλκ° λλ½λκ±°λ νμ
μ΄ λ€λ₯Έ λ°μ΄ν°κ° λ€μ΄μλ any λλ¬Έμ μ»΄νμΌ μμ μ μ€λ₯λ₯Ό κ°μ§νμ§ λͺ»ν©λλ€.
processUserData({ id: '456', username: 'Bob', status: 'inactive' }); // 'name', 'lastLogin', 'address' νλ μμ
processUserData({ id: '789', name: 'Charlie', status: 'pending', lastLogin: 'yesterday' }); // 'lastLogin'μ΄ string
// μ νΈμΆλ€μ λ°νμμ μλ¬λ₯Ό λ°μμν€κ±°λ μμμΉ λͺ»ν λμμ ν μ μμ΅λλ€.
After Code (Good)
// β¨ κ°μ λ μ½λ: λͺ
μμ μΈ νμ
κ³Ό unknown, μ λ€λ¦ νμ©
interface UserProfile {
id: string;
name: string;
email?: string; // μ νμ μμ±
status: 'active' | 'inactive' | 'pending';
lastLogin?: number; // νμμ€ν¬ν (μ νμ )
address?: { // μ£Όμλ μ νμ , λ΄λΆ ꡬ쑰 λͺ
μ
street: string;
city: string;
}
}
// λͺ
νν μΈν°νμ΄μ€λ₯Ό μ¬μ©νμ¬ νμ
μμ μ± ν보
function processStrictUserData(data: UserProfile): string {
console.log(`User ID: ${data.id}`);
console.log(`User Name: ${data.name}`);
// μ΅μ
λ 체μ΄λκ³Ό νμ
κ°λλ₯Ό μ¬μ©νμ¬ μμ νκ² μ κ·Όν©λλ€.
if (data.address?.street) {
console.log(`User Address: ${data.address.street}`);
}
if (data.status === 'active' && data.lastLogin) {
// lastLoginμ΄ numberλ‘ λ³΄μ₯λλ―λ‘ μμ νκ² μ¬μ©ν©λλ€.
console.log(`Last login: ${new Date(data.lastLogin).toLocaleString()}`);
}
return data.id;
}
// β¨ μ΄μ μ»΄νμΌ μμ μ νμ
μλ¬λ₯Ό κ°μ§ν©λλ€!
processStrictUserData({
id: '123',
name: 'Alice',
status: 'active',
lastLogin: 1678886400000,
address: { street: 'Main St', city: 'Anytown' }
});
// Error: Argument of type '{ id: string; username: string; status: string; }' is not assignable to parameter of type 'UserProfile'.
// Object literal may only specify known properties, and 'username' does not exist in type 'UserProfile'.
// processStrictUserData({ id: '456', username: 'Bob', status: 'inactive' });
// unknown νμ
μ μ¬μ©νμ¬ λ μμ ν λ°μ΄ν° μ²λ¦¬ (μΈλΆμμ μ€λ λΆνμ€ν λ°μ΄ν°μ μ ν©)
function processUnknownUserData(rawData: unknown) {
// rawDataκ° UserProfile νμ
μΈμ§ λ°νμμ μ격νκ² κ²μ¦ν©λλ€.
if (
typeof rawData === 'object' && rawData !== null &&
'id' in rawData && typeof rawData.id === 'string' &&
'name' in rawData && typeof rawData.name === 'string' &&
'status' in rawData && (rawData.status === 'active' || rawData.status === 'inactive' || rawData.status === 'pending')
) {
const userData: UserProfile = rawData as UserProfile; // νμ
κ°λλ₯Ό ν΅κ³ΌνμΌλ―λ‘ μμ νκ² λ¨μΈ
console.log(`Processing unknown user: ${userData.name}, Status: ${userData.status}`);
} else {
console.warn('Received invalid user data format or missing required fields.');
}
}
processUnknownUserData({ id: '111', name: 'David', status: 'active' });
processUnknownUserData({ userId: '222', userName: 'Eve' }); // 'id'μ 'name', 'status' νλ κ²μ¦μ μ€ν¨νμ¬ κ²½κ³ λ©μμ§ μΆλ ₯
// μ λ€λ¦ νμ© μμ (λ μ μ°νκ² νμ
μμ ν ν¨μ μμ±)
interface ApiResponse<T> {
statusCode: number;
message: string;
data: T;
}
function handleApiResponse<T>(response: ApiResponse<T>): T {
if (response.statusCode === 200) {
return response.data;
} else {
throw new Error(response.message);
}
}
interface Product {
productId: string;
productName: string;
price: number;
}
const productResponse: ApiResponse<Product> = {
statusCode: 200,
message: 'Success',
data: { productId: 'P001', productName: 'Laptop', price: 1200 }
};
const product = handleApiResponse(productResponse);
console.log(product.productName); // 'product'λ Product νμ
μΌλ‘ μΆλ‘ λ©λλ€.
// const errorResponse: ApiResponse<Product> = {
// statusCode: 404,
// message: 'Product not found',
// data: { productId: 'P002', productName: 'Monitor', price: 300 } // Tκ° Productμ΄λ―λ‘ dataλ Product νμ
μ΄μ΄μΌ ν¨
// };
// try {
// handleApiResponse(errorResponse);
// } catch (e) {
// console.error(e.message);
// }