[타입스크립트] any와 제네릭: 타입 안정성의 차이점과 올바른 사용법
현업에서도 제네릭 타입을 많이 쓰지만 정확히 짚고 넘어간 적은 없었기에 이번 기회에 정리해보려고 한다.
서론:
JavaScript의 동적 타이핑과 타입 시스템을 더 안전하게 다루기 위한 TypeScript의 등장. TypeScript는 강력한 타입 시스템을 제공하면서도, 유연성을 잃지 않도록 다양한 방법을 제공합니다. 그 중에서 any 타입과 제네릭은 유연한 타입 처리 방식을 제공하는 두 가지 중요한 기능입니다. 하지만 이 두 가지는 타입 안전성을 어떻게 다루는지에서 중요한 차이점이 있습니다. 이번 포스트에서는 any 타입과 제네릭이 무엇인지, 그리고 이들의 차이점과 적절한 사용법을 다뤄보겠습니다.
1. any 타입이란?
any 타입은 타입을 명시하지 않거나, 어떤 타입도 허용한다. 이는 typeScript만 가지는 특별한 타입이다.
이 타입을 사용하면 typeScript 의 타입 검사를 우회할 수 있으며, 어떤 값이든 할당할 수 있게 된다. any 타입은
한마디로 any 타입을 도배한다면 타입스크립트를 쓰는 이유가 업게 된다.
예시 코드)
let value: any;
value = 10; // number 타입
value = "hello"; // string 타입
value = true; // boolean 타입
위 코드에서 value 는 어떤 타입이든 허용하기 때문에 타입 추론이 일어나지 않으면 타입 체크도 수행되지 않음
이는 타입 안전성을 완전히 포기하는 것이므로, 실수로 잘못된 타입을 사용할 경우 런타임 오류가 발생할 수 있습니다.
문제점
function add(a: any, b: any): any {
return a + b;
}
let result = add(10, "20"); // 문자열과 숫자를 더하는 코드
console.log(result); // "1020" (예상치 못한 동작)
위 코드에서 add 함수는 any 타입을 사용하므로, 숫자와 문자열을 더하는 의도치 않은 동작이 발생할 수 있습니다.
2. 제네릭 타입이란?
타입을 동적으로 지정할 수 있게 해주는 typeScript의 기능이다. 제네릭을 사용하면 타입을 유연하게 다룰 수 있으며,
타입 추론과 검사를 계속 유지할 수 있습니다. 제네릭은 any와 다르게 타입 안정성을 보장하면서도 동적 타입 지정이 가능하게 된다.
예시 코드)
function identity<T>(arg: T): T {
return arg;
}
const result1 = identity(10); // T는 number로 추론
const result2 = identity("hello"); // T는 string으로 추론
이처럼 identity 함수는 동적으로 타입을 받아들인다. T는 호출 시점에서 적절한 타입으로 추론되며 타입 안전성을 유지하면서도 유연하게 타입을 다룰 수 있습니다. 타입 추론과 검사는 여전히 가능하며, 잘못된 타입을 사용하면 컴파일 오류가 발생합니다.
3. any 와 제네릭의 차이점
1) 타입 안전성
- any: any는 타입 검사를 하지 않기 때문에 타입 안전성을 잃게 됩니다. 어떤 타입이든 할당할 수 있기 때문에, 코드에서 런타임 오류를 발생시킬 가능성이 있습니다.
- 제네릭: 제네릭은 타입 안전성을 유지하면서 유연하게 타입을 다룰 수 있게 해줍니다. 제네릭은 타입 검사를 수행하므로, 잘못된 타입을 사용하면 컴파일 타임 오류가 발생합니다.
2) 타입 추론
- any: any는 타입 추론을 무시하고, 어떤 값이든 할당할 수 있습니다. 따라서 타입 검사가 이루어지지 않습니다.
- 제네릭: 제네릭은 타입 추론을 유지하면서도 동적으로 타입을 설정할 수 있습니다. 제네릭을 사용하면 타입 안전성을 확보하면서도 유연성을 제공합니다.
3) 유연성
- any: any는 완전한 자유를 제공합니다. 그러나 그만큼 타입 안전성을 포기하므로, 잘못된 값이 할당될 수 있습니다.
- 제네릭: 제네릭은 유연성을 제공하면서도 타입 안전성을 유지합니다. 타입을 동적으로 받으면서도 타입 오류를 예방할 수 있습니다.
그렇다면 언제 any를 사용하고 언제 제네릭을 써야 할까?
| any를 사용할 때 | 제네릭을 사용할 때 |
| 외부 라이브러리 혹은 API에서 타입을 확인할 수 없을 때 사용 | 타입 안정성을 유지하면서 유연하게 타입을 다루고자 할 때 |
| 빠른 프로토타입을 만들 때, 타입 검사 없이 자유롭게 값을 처리하고 싶을때 사용 | 여러 타입을 처리해야 할 때, 타입 추론을 통해 안전하게 코드를 작성하고자 할 때 |
| 임시로 타입을 우회하고자 할 때 사용 | 컴파일 타임에 타입 검사를 통해 런타임 오류를 방지하고 싶을 때. |
결론
any와 제네릭은 타입을 유연하게 다루는 방식으로 TypeScript에서 매우 유용한 기능이지만, 타입 안전성을 유지하는 것이 TypeScript를 사용하는 주된 이유입니다. any를 과도하게 사용하면 TypeScript의 주요 장점인 타입 검사를 잃게 되므로, 타입 안전성을 유지하면서도 유연하게 타입을 다룰 수 있는 제네릭을 적극적으로 사용하는 것이 좋습니다.
타입 시스템을 잘 활용하면 코드의 안전성과 유지보수성이 향상됩니다. any는 최소화하고, 제네릭을 적절히 사용하여 타입 안전성을 지키며 개발하는 것이 중요합니다.