자바스크립트에서 NOT ! 연산자는 false를 의미한다. 반면 타입스크립트에선 이 느낌표 ! 연산자가 변수 뒤에 올 수 있다. if문 등을 사용하지 않고 항상 유효한 값이 있다고 단언 할 때 사용한다.

Non-null assertion operator (느낌표)


<aside> 💡 ESLint 규칙에선 느낌표 연산자를 사용하면 null-checking mode의 혜택을 받지 못하기 때문에 권장하지 않는다. 대신 foo.bar && foo.bar... AND 연산자 혹은 옵셔널 체이닝 사용을 권장한다. 느낌표 연산자를 꼭 사용해야 한다면 항상 유효한 값을 보장하는 변수에만 사용한다.

</aside>

변수 내부 값에 접근할 때 TS 컴파일러는 항상 null undefined 인지 체크한다. 이때 if 문으로 타입 단언을(변수에 원하는 타입을 강제로 부여)을 할 수 있다.

매번 if 문 쓰는게 귀찮을 때 ! 연산자를 사용할 수 있다. 그럼 해당 값이 null이나 undefined가 아니라고 단언해준다. 컴파일러에게 이 값은 절대 null 혹은 undefined가 될 수 없으니 실행하라는 것과 비슷하다.

아래 코드의 콘솔을 실행하면 TS2531 에러가 발생한다. lastName 타입을 string 혹은 null로 설정했기 때문에 해당 값이 null일 수도 있어서 나오는 경고다.

interface User {
  firstName: string;
  lastName: string | null;
}

const user1: User = {
  firstName: 'Cobain',
  lastName: 'Kurt',
};

console.log(user1.lastName.toUpperCase()) 
// 에러! TS2531: Object is possibly 'null'.

아래처럼 lastName 뒤에 ! 느낌표를 붙여서 항상 값이 존재한다고 단언하면 에러가 나오지 않는다.

console.log(user1.lastName!.toUpperCase()) // 'Kurt'

이와 비슷한 옵셔널 체이닝(변수뒤에 물음표 ? 연산자 사용)은 해당 값이 null 혹은 undefined면 평가를 멈추고 undefined 를 반환하는 점이 다르다. 아래처럼 평가하는 값이 null이나 undefined일 수도 있을 땐 에러를 피하기 위해 옵셔널 체이닝을 사용한다.

const user1: ServiceUser = {
  firstName: 'Cobain',
  lastName: null,
};

console.log(user1.lastName!.toUpperCase()); // 에러! Cannot read properties of null (reading 'toUpperCase')
console.log(user1.lastName?.toUpperCase()); // undefined

Definite assignment assertions


변수 뒤에 느낌표 연산자 !를 사용해서 값이 할당되어 있지 않은 변수나 객체를 사용할 수도 있다. 컴파일러에게 해당 변수의 값은 항상 할당되어 있다고(아직 값을 할당하지 않았어도) 알려주는 것이라고 보면 된다. 이를 Definite Assignment Assertions 라고 부른다.

일반적인 경우 값을 할당하지 않고 사용하면 아래처럼 TS2454 에러가 발생한다.

let num: number;
console.log(num) // 에러! TS2454: Variable 'num' is used before being assigned.

num 변수 뒤에 느낌표 !를 붙이면 에러가 발생하지 않는다. 컴파일러에게 변수 num은 항상 값이 할당되어 있다고 단언(assertion)했기 때문이다.

let num!: number;
console.log(num) // undefined