<Demopeu/>

타입 단언(Type-Assertions)

타입 단언(Type-Assertions)

referencetypescripttype-assertionsas

as 타입 : 지금 이 값은 이 타입이야 믿어줘!

타입스크립트가 추론한 타입 대신, 내가 지정한 타입으로 강제로 간주하게 만드는 문법이다.

컴파일 타임 타입 검사를 우회하기 위해 사용하는 문법으로 꽤나 위험할 수 있다.

💡 사용 예시

1. 구조 미완성 객체를 강제로 특정 타입으로 만들 때

type Person = {
  name: string;
  age: number;
};

let person: Person = {};
// ❌ Error: '{}' 형식은 'Person' 형식에 필요한 name, age 프로퍼티가 없습니다.

person.name = "";
person.age = 30;

위 코드는 Person 타입에 필요한 프로퍼티가 없는 {}를 바로 할당했기 때문에 에러가 난다.

이럴 때 타입 단언을 사용하면:

type Person = {
  name: string;
  age: number;
};

let person = {} as Person;

person.name = "";
person.age = 30;

{} as Person
"지금은 빈 객체지만, 어쨌든 이걸 Person이라고 치고 넘겨"
라는 의미다.

⚠️ 하지만 이는 컴파일러의 안전장치를 해제하는 것이므로
가능하면 처음부터 Person 형태로 객체를 만드는 편이 더 안전하다.

2. 초과 프로퍼티 검사를 피할 때

type Person = {
  name: string;
  age: number;
};

const person: Person = {
  name: "John",
  age: 30,
  location: "New York",
};
// ❌ Error: 'location' 프로퍼티가 'Person' 형식에 없습니다.

직접 Person 타입 변수에 객체 리터럴을 넣으면 location처럼 선언되지 않은 프로퍼티는 에러가 난다. 이게 바로

초과 프로퍼티 검사(Excess Property Checking)

이때 타입 단언을 쓰면 검사를 우회할 수 있다.

type Person = {
  name: string;
  age: number;
};

const person = {
  name: "John",
  age: 30,
  location: "New York",
} as Person;

이때 as Person을 사용하면
"이 객체에 여분의 프로퍼티가 있어도 신경 쓰지 말고 Person 타입으로 그냥 받아들여"
라는 의미가 된다.

⚠️ 이 패턴 역시 진짜로 필요할 때만 쓰는 게 좋다. 대부분은 타입 정의를 수정하거나, 별도 타입을 만드는 쪽이 더 안전하다.

📌 타입 단언의 주의사항

"값" as "타입"으로 표현 시,

  • "값"은 "타입"의 슈퍼 타입이다.
  • "값"은 "타입"의 서브 타입이다.
let num1 = 10 as never; // ✅
let num2 = 10 as unknown; // ✅

let num3 = 10 as string; // ❌

10(리터럴 타입)과 string은 서로 서브타입/슈퍼타입 관계가 아니다.

따라서 타입 단언시, 최소한 타입 간에 "서브타입/슈퍼타입 관계"가 있어야 통과한다.

이걸 억지로 넘기고 싶다면?

let num1 = 10 as unknown as string; // ✅

중간에 값을 unknown 타입으로 단언하면 unknown 타입은 모든 타입의 슈퍼타입이므로 모든 타입으로 또 다시 단언하는게 가능해진다.

⚠️ 이 순간부터는 타입 안전성을 거의 포기하는 셈이라 정말 조심해야 한다.

📌 const 단언

as const변수를 const로 만드는 문법은 아니다.
특정 "값"을 최대한 구체적인 리터럴 타입으로 만들고,
객체/배열인 경우에는 readonly로 고정하는 타입 단언이다.

let num = 10 as const;
num = 20; // ❌ Error: '20' 타입은 '10' 타입에 할당될 수 없습니다.

let을 const 처럼 만들어 재할당 자체를 막는 것이 아니라 값 자체의 타입을 좁혀서 고정하는 것.

📌 Non Null 단언

non null 단언은 값이 null 또는 undefined가 될 수 없다는 것을 컴파일러에게 알려주는 문법. as 타입이 아니라 값 뒤에 !를 붙여서 표현한다.

const el = document.querySelector("#title")!;
// el은 HTMLElement | null이 아니라, HTMLElement라고 간주된다.

⚠️ 실제로 null일 수도 있는 상황에서 무조건 !를 쓰면, 런타임에서 바로 에러가 터질 수 있으니, 조건문으로 null 체크가 가능한 경우에는 체크를 먼저 하는 편이 더 안전하다.

🤔 왜 as 같은 문법이 생겼을까?

구조적 한계로 나온 탈출구 문법

TypeScript는 처음부터 설계된 언어가 아니라,
이미 돌아가던 자바스크립트 위에 타입 레이어를 얹은 언어다.

그래서 런타임에는 문제 없는데,
정적인 타입만으로는 표현하기 애매한 것들이 많다.

특히 외부에서 들어오는 값들 — DOM, JSON, window 전역 변수, 서버 응답 등 — 은
실제 값은 런타임에만 확정되기 때문에 컴파일러가 전부 알 수 없다.

const title = document.querySelector("#title");

직접 비교해 보면,

  • 실제 상황: "내가 index.html 관리하고 있어서 #title이 무조건 있다는 걸 개발자가 알고 있음"

  • TypeScript 입장: Element | null(진짜로 존재하는지, 컴파일러는 모름)

이걸 완전하게 타입으로 증명하려면 너무 복잡한 정석 분석 필요해서, 현실성이 없음.

그래서 개발자가 책임 져라라는 탈출구를 줌.

정리하면,

  1. 태생이 JS라 애매한게 많음.
  2. 현실적으로 타입 시스템이 모든걸 알 수 없음.
  3. 통신 할 때 "무조건 애매한 타입"이 튀어나옴.

🤖 AI가 as 문법을 남발하는 이유

대 AI 시대에 심심치 않게 as를 볼 수 있다. 그런데 as는 항상 좋은 해결책이 아닌데, 왜 이렇게 많이 쓰일까?

1. AI는 프로젝트의 전체 타입 정보를 모른다

  • tsconfig 설정을 체크하지 않는 경우
  • 전역 타입 선언을 체크하지 않는 경우
  • 실제 빌드/런타임 환경을 체크하지 않은 경우
  • global 룰을 부정확하게 설계하거나 무시하는 경우

이러한 경우로 인해 AI는 일단 에러가 안 나게 하는 코드 패턴으로 덮어버린다.

2. AI가 학습한 예제 코드들 중에서 클린 코드는 많지 않다

사람이 만든 코드도 일단 as로 통과시키고 보자 하는 스타일이 엄청 많은데 이걸 학습해버림.

✅ 결론

as는 "나쁜 문법"이라기보다, 타입 시스템의 한계를 메꾸는 필수 탈출구

그러나 귀찮을 때 남용하기 좋은 문법이기 때문에 as를 만나면 한 번 더 의심하는 연습을 하자.