타입스크립트의 Soundness란?

Sheryl Yun·2022년 10월 5일
0

타입스크립트

목록 보기
4/11

Soundness(건전성)

타입스크립트에서 기본적으로 타입 안정성을 지키려고 하는 것을 말한다.

타입스크립트는 컴파일 시점에 타입을 추론할 수 없는 특정 타입에 대해서 '일단 안전하다'고 보는 특성이 있다.

언어의 설계는 단순성, 사용성, 건전성의 절충이다.
타입스크립트가 자바스크립트와의 호환과 사용성, 단순성을 위해 건전성을 절충한 부분불건전성(UnSoundness)이라고 한다.

타입스크립트의 불건전한 경우들은 타입의 안정성을 해칠 수 있기 때문에 꼭 필요한 경우에만 유의해서 사용해야 한다.

불건전한 경우 예시

1. 타입 단언

개발자가 타입을 단언해서 타입스크립트에게 알려주는 것이다.
이는 타입을 이렇게 지정한 것에 대해 개발자가 책임을 지겠다는 뜻이다.

이러한 자의적인 지정이 좋은 방법은 아니기 때문에 다음 대안들을 제시한다.

대안 1 : 타입 narrowing (if문)

const x1 = Math.random() || null;  // '||'로 연결되어서 어느 타입일지 확신할 수 없는 경우,

// if문을 통해 '타입이 null이 아닌 경우'로 좁혀준다
if (x1 !== null) {
  alertNumber(x1);  // => 둘 중 number 타입으로 확정된 상황에서 실행
}

대안 2 : 타입 가드

&&으로 이어진 조건들이 모두 true일 때만 다음 실행하는 것
true를 확인하는 부분을 함수로 만들 수도 있다.

// 타입 가드 함수
function isFunFact(data: unknown): data is FunFact {
  return data && typeof data === 'object' && 'fact' in data /* && ... */;
}

// API 호출
const response = await fetch('/api/fun-fact');
const fact = await response.json();

// 타입 가드 함수를 통과하지 못하면 에러
if (!isFunFact(fact)) {
  throw new Error(`Either it wasn't a fact or it wasn't fun`);
}

// 위의 if문을 통과하면 fact의 타입이 FunFact로 확정된다

2. 배열에 없는 요소를 조회할 때

const xs = [0, 1, 2];
const x = xs[3];  // undefined;

타입스크립트는 배열에 없는 요소는 타입 추론을 해주지 않는다.
그래서 xs[3]의 타입은 number로 유추되지만 런타임의 결과는 undefined이다.

타입이 모두 추론 가능해야 '건전(sound)'한데 그렇지 않아서 불건전한 경우이다.

객체 조회에서도 이런 일이 발생한다.
피할 수 있는 방법은 객체 참조로 전달하여 직접적인 조회를 피하는 것이다.

interface MenuProps {
  menuItems: { [id: string]: MenuItem }; // '[ ]' 리터럴로 키와 value의 타입 지정
  onSelectItem: (menuItem: MenuItem) => void;
}

3. any

어떤 타입이든 허용하는 키워드여서 타입 건전성을 위해서는 의도적으로 피해야 한다.

any 타입 값을 함수의 인수로 넣으면
함수 실행 시 타입스크립트가 알아서 함수의 인자에 지정된 타입으로 좁혀주지 않는다. (에러 발생)

function alertNumber(x: number) {
  alert(x.toFixed(1));  // static type of x is number, runtime type is string
}

const num: any = 'forty two';
alertNumber(num);
// 실행하면 컴파일 때는 에러가 없다가, 런타임 때 다음 에러를 던짐 
// Cannot read property 'toFixed' of undefined

any는 '타입 검사를 아예 하지 않는다'는 말과 동일하다.
따라서 any보다는 unknown이 낫다. (어떤 타입이 들어올지 모른다는 뜻)

예외

반드시 any를 리턴하는 경우도 있다. (JSON.parse)

JSON.parse("{ a: 1 }"); 

4. Rest 매개변수

타입스크립트에서 Rest 매개변수는 모두 optional한 값으로 간주된다.
따라서 콜백에 들어가는 매개변수의 정확한 갯수를 알 수 없게 된다.

function getRandomNumbers(count: number, callback: (...args: number[]) => void) { }

// 2항의 콜백함수에 매개변수(...args)가 2개든 1개든 오류가 나지 않음 (number라고 타입만 아는 상태)
getRandomNumbers(2, (first, second) => console.log([first, second]));
getRandomNumbers(400, (first) => console.log(first));

5. void 리턴 함수

다른 타입을 리턴하는 함수가 인자로 들어오는 것을 허용하기 때문에 불건전하다.

const getPI = () => 3.14;

function runFunction(func: () => void) {
  func();
}

runFunction(getPI);

참고

Soundness란? - 캡틴판교 님
타입스크립트의 타입 불건전성에 대하여 1 : 타입시스템의 한계
타입스크립트의 타입 불건전성에 대하여 2 : 공식 예제 코드 살펴보기

profile
데이터 분석가 준비 중입니다 (티스토리에 기록: https://cherylog.tistory.com/)

0개의 댓글