타입스크립트 형변환의 모든 것: 완벽 정복 가이드
타입스크립트를 사용하다 보면 컴파일러가 타입을 제대로 추론하지 못하거나, 개발자가 더 정확한 타입을 알고 있을 때가 있습니다. DHH(David Heinemeier Hansson)가 hotwired/turbo 프로젝트에서 타입스크립트를 제거하면서 "타입스크립트가 정말 필요한가?"에 대한 논쟁이 다시 불붙기도 했죠. 하지만 대규모 프로젝트에서 타입스크립트의 정적 타입 시스템이 주는 안정성은 여전히 매력적입니다.
이러한 타입 시스템을 더 유연하고 강력하게 사용하려면 ‘형변환(Type Casting)’ 또는 '타입 단언(Type Assertion)'에 대한 이해가 필수적입니다. 이 글에서는 타입스크립트의 다양한 형변환 방법을 알아보고, 각 상황에 맞는 최적의 방법을 선택하는 기준을 제시해 드립니다.
먼저 중요한 점을 짚고 넘어가겠습니다. 타입스크립트의 형변환은 런타임에 값의 실제 형태를 바꾸는 것이 아닙니다. 오직 컴파일 타임에 타입 검사기에게 "이 값은 내가 명시하는 이 타입으로 간주해 줘"라고 알려주는 행위입니다. 런타임 코드에는 아무런 영향을 주지 않죠.
as: 가장 기본적인 타입 단언
as 키워드는 타입스크립트에서 가장 흔하게 사용되는 타입 단언 문법입니다. 개발자가 컴파일러보다 타입을 더 잘 알고 있을 때 확신을 가지고 타입을 지정해 줄 수 있습니다. 특히 any나 unknown처럼 타입 정보가 없는 값을 구체적인 타입으로 다룰 때 유용하게 사용됩니다.
interface Hero {
name: string;
age: number;
}
const capt = {} as Hero; // 빈 객체를 Hero 타입으로 단언
capt.name = '캡틴';
capt.age = 100;
참고로, JSX(TSX)와 함께 사용할 때는 꺾쇠 괄호(<Type>) 문법이 JSX 태그와 혼동될 수 있으므로, as 키워드를 사용하는 것이 표준처럼 굳어졌습니다. 꼭 기억해두세요!
as const: 값을 리터럴 타입으로 고정하기
as const는 변수나 객체를 마치 const로 선언한 것처럼 만들어, 모든 프로퍼티를 readonly로 바꾸고 값을 아주 구체적인 리터럴 타입으로 고정합니다.
// 일반적인 경우, 'direction'의 타입은 'string'으로 추론됩니다.
let direction = "left";
// 'as const'를 사용하면 타입이 "left"라는 리터럴 타입으로 고정됩니다.
const directionConst = "left" as const;
// directionConst의 타입: "left"
const config = {
method: "GET",
cache: false,
} as const;
/*
config의 타입:
{
readonly method: "GET";
readonly cache: false;
}
*/
이렇게 하면 객체의 프로퍼티가 실수로 변경되는 것을 막을 수 있고, 특정 문자열 값만 허용하는 함수에 인자로 넘길 때 타입 안정성을 높일 수 있습니다.
!: Non-null 단언 연산자
! 연산자는 특정 값이 null이나 undefined가 절대 아니라고 컴파일러에게 강하게 주장할 때 사용합니다. DOM 요소에 접근할 때처럼, 개발자는 해당 요소가 반드시 존재한다고 확신하는 상황에서 유용합니다.
// 컴파일러는 querySelector가 null을 반환할 수 있다고 경고합니다.
const el = document.querySelector("input")!; // '!'로 null이 아님을 단언
el.focus(); // 이제 el이 null이 아니라고 간주하므로 오류가 발생하지 않습니다.
하지만 이 방법은 신중하게 사용해야 합니다. 만약 런타임에 실제로 값이 null이라면, 코드는 에러를 발생시키며 멈추게 됩니다. !는 컴파일러의 유용한 경고를 무시하는 행위이므로, 정말 100% 확신할 수 있는 상황에서만 제한적으로 사용하는 것이 좋습니다.
satisfies: 타입은 지키고 추론은 살리고
ES2022에 도입된 satisfies 연산자는 as의 단점을 보완하는 아주 똑똑한 방법입니다. 어떤 값이 특정 타입을 "만족(satisfy)"하는지 검사하면서도, 값 자체의 구체적인 리터럴 타입 추론은 그대로 유지해 줍니다.
as를 사용하면 타입이 강제로 고정되어 원래의 구체적인 타입 정보를 잃게 될 수 있습니다. 하지만 satisfies는 타입 검사를 통과시키면서도 타입 추론의 이점을 모두 누리게 해줍니다.
type Config = { method: "GET" | "POST"; url: string };
// 'as'를 사용한 경우
const apiConfigAs = {
method: "GET",
url: "/users",
} as Config;
// apiConfigAs.method의 타입은 "GET" | "POST"으로 확장됩니다.
// 'satisfies'를 사용한 경우
const apiConfigSatisfies = {
method: "GET",
url: "/users",
} satisfies Config;
// apiConfigSatisfies.method의 타입은 "GET" 리터럴 타입으로 유지됩니다!
만약 객체에 Config 타입에 없는 프로퍼티를 추가하면 satisfies는 컴파일 에러를 발생시켜 실수를 막아줍니다. 이처럼 타입 검사와 타입 추론의 장점을 모두 취하고 싶을 때 satisfies는 최고의 선택이 될 수 있습니다.
이중 단언 (Double Assertion)
서로 관련 없는 타입끼리는 직접 형변환이 불가능합니다. 이럴 때 unknown (또는 any)을 중간 다리로 사용하는 ‘이중 단언’ 기법을 사용할 수 있습니다.
const value: string = "123";
// 오류: string 타입을 number 타입으로 직접 변환할 수 없습니다.
// const num = value as number;
// 이중 단언: string -> unknown -> number
const num = value as unknown as number; // 위험하지만, 컴파일러를 통과합니다.
이 방법은 타입 검사기를 완전히 우회하는 것이므로 매우 위험합니다. 코드가 실제로 무엇을 하는지 정확히 알고 있으며, 다른 방법이 없을 때 최후의 수단으로만 사용해야 합니다. 대부분의 경우 사용자 정의 타입 가드(아래에서 설명) 등으로 더 안전하게 해결할 수 있습니다.
is: 사용자 정의 타입 가드로 타입 좁히기
조건문을 사용해 타입을 좁히는 것을 '타입 가드(Type Guard)'라고 합니다. typeof, instanceof 등이 대표적이죠. 여기서 더 나아가 is 키워드를 사용하면 사용자 정의 타입 가드 함수를 만들 수 있습니다. 복잡한 객체의 타입을 판별하는 로직을 함수로 분리하여 코드의 재사용성과 가독성을 높일 수 있습니다.
// 'value is string'은 이 함수가 true를 반환하면 value의 타입이 string임을 보장한다는 의미입니다.
function isString(value: unknown): value is string {
return typeof value === "string";
}
function print(value: unknown) {
if (isString(value)) {
// 이 블록 안에서 value는 string 타입으로 안전하게 취급됩니다.
console.log(value.toUpperCase());
}
}
이처럼 is를 활용하면 복잡한 조건부 타입 로직을 명확하고 안전하게 관리할 수 있습니다.
더 나은 코드를 위한 제언
타입스크립트의 형변환은 강력한 도구이지만, 남용하면 타입 시스템의 장점을 무너뜨릴 수 있습니다. "나는 컴파일러보다 똑똑하다"는 자신감이 필요할 때도 있지만, 대부분의 경우 컴파일러의 경고에는 이유가 있습니다.
형변환을 사용하기 전에 다음을 먼저 고려해 보세요.
- 타입 가드(
typeof,instanceof,in또는 사용자 정의is함수)로 타입을 안전하게 좁힐 수 없는가? satisfies연산자를 통해 타입 검사와 타입 추론을 모두 만족시킬 수는 없는가?- 제네릭(Generic)을 사용해 더 유연한 타입 구조를 만들 수는 없는가?
이러한 고민을 거친 후에도 여전히 형변환이 필요하다면, 그 의미와 잠재적 위험을 정확히 이해하고 사용하시길 바랍니다 ! 올바르게 사용된 타입 단언은 여러분의 타입스크립트 코드를 한 단계 더 높은 수준으로 이끌어 줄 것입니다.
타입 스크립트를 사용 하는데에 두 가지 명언이 있습니다.
any를 사용하지 말 것.- 타입 단언을 사용하지 말것.
그렇지만 정말 확실할 때에 타입 단언을 사용하는 것은 코드 작성을 훨씬 빠르게 단축시킬 수 있죠 !