작성자와 사용자의 관점으로 코드 바라보기

eeensu·2023년 7월 30일
0

typescript

목록 보기
6/22
post-thumbnail

ts의 기능을 이용하는 데 있어서, 타입의 작성자와 사용자를 구분하도록 해보자.
만약 혼자 기획하고 개발하고 배포하는 1인 개발자인 경우, 타입의 작성자와 사용자가 동일하다.

하지만 대부분의 상황에서는 팀원과의 협업이 주로 이루어지거나, 내가 만들어 놓은 타입을 다른 사람이 사용하는 경우가 있다. 특정 라이브러리에서 제작된 타입을 라이브러리를 응용하는 개발자들이 사용하는 경우가 예시로 들 수 있다.




타입 시스템

타입이란 해당 변수가 할 수 있는 일을 결정한다. js에서 예시를 들어보면 f1이라는 함수에서 함수의 body에서는 a를 사용할 것이다. a가 할 수 있는 일은 a의 타입이 결정한다.

function f1(a) {
  reurn a
}

타입 시스템에는 컴파일러에게 사용하는 타입을 명시적으로 지정하는 시스템과 컴파일러가 자동으로 타입을 추론하는 시스템이 있다. 개발자가 타입을 명시적으로 지정하지 않으면, ts 컴파일러가 자동으로 타입을 추론하는 형태로 운영된다. 이를 ts의 타입 추론이라고 한다.


하지만 함수의 사용자가 사용법을 충분히 숙지하지 않은 채, 문자열을 사용하여 함수를 실행한다면? 아래와 같은 사용은 오해를 야기할 수 있다.

function f2(a) {
        reurn a * 2;
}

console.log(f2(10));                 // 20
console.log(f2('Mark'))              // NaN

이렇게 간단한 함수에서 잘못 사용하는 것은 힘들다고 생각할 수 있지만, 함수의 길이가 길어지고, 그러한 함수들이 같이 사용되면, 이러한 상황은 충분히 발생될 수 있을 것이다. 이러한 상황을 방지하기 위해 ts에서는 처음부터 함수의 인자와 리턴의 타입을 명시해 줌으로써 에러를 사전에 방지해 준다.

또한 ts는 타입 추론을 할 때, 어떤 타입으로도 추론이 될 수 없으면 any로 추론한다. any는 앞서 설명한 대로, 사용에는 편하지만 위험한 타입이다. 때문에 ts에서 자동으로 타입을 정해주는 타입 추론 기능이 있다 하더라도, 개발자가 명시적으로 타입을 지정해 주는 것이 좋다.



예외

ts의 타입 추론은 100% 완벽하지 않다. 다음과 같은 상황이 있다.

function f3(a: number){
  if (a > 0) return a * 3;
} 

console.log(f3(5));                           // 15
console.log(f3(-5) + 5);                      // NaN

위 함수에서 매개변수의 타입을 지정해 주었지만 함수의 리턴 타입은 2가지이다.
a가 양수로 올 때의 number와 그렇지 않을 때의 undefined 이다. 때문에 결과가 NaN인 f(-5) + 5 는 함수 작성자의 의도와 다르기 때문에 오류이다. 하지만 nullundefined는 기본적으로 모든 타입들의 서브타입이기 때문에, 위의 과정은 오해를 야기할 수 있다.

때문에 컴파일 과정에서, 모든 타입에 자동으로 포함되어 있는 nulluindefined를 제거하고 싶으면 strictNullCheck 옵션을 true로 설정해 준다. 이 옵션을 사용하면 true로 설정해 준다. (default는 true이다.)

또한 if로 나누어진 상황에서, 모든 조건에서 return이 발생하지 않으면 에러를 발생시켜주는 옵션인 noImplicitAny 도 있다. 이를 켜주면 ts가 추론 중 함수 내에서 모든 코드가 값을 리턴하지 않거나 any라고 판단하게 되면, 컴파일 에러를 발생시켜 명시적으로 지정하도록 유도한다.




공변성과 반공변병

공변성과 반공변성은 타입 시스템에서의 타입 호환성을 다루는 중요한 원칙이며, 이를 이해하면 배열, 함수, 메서드 인터페이스 등을 다룰 때 타입 호환성이 어떻게 동작하는지 더 잘 이해할 수 있다. ts는 기본적으로 공변성을 따르도록 옵션이 활성화 되어있기에 반공병성은 에러를 일으킨다. 하지만 해당 옵션을 비활성화하면 두 특성 모두 따를 수 있다.

  • 공변성 (Covariance)
    공변성은 주로 배열이나 함수와 같은 복합 타입에서 나타 만약 A 타입이 B 타입의 하위 타입(또는 같은 타입)이고, 배열이나 함수가 반환값의 타입에 영향을 미치지 않는다면, A 타입의 배열이나 함수는 B 타입의 배열이나 함수에 할당 가능하다.
let arrA: number[] = [1, 2, 3];
let arrB: number[] = arrA;		 // 가능 (공변성)
  • 반공변성 (Contravariance):
    반공변성은 함수의 매개변수 타입에서 나타난다. 만약 A 타입이 B 타입의 하위 타입(또는 같은 타입)이고, 함수의 인자에 B 타입을 받아들이는 것이 A 타입을 받아들이는 것보다 더 제한적이라면, B 타입의 함수는 A 타입의 함수에 할당 가능하다.
type FuncA = (x: number) => void;
type FuncB = (x: string) => void;

let fnA: FuncA = (x) => console.log(x);
let fnB: FuncB = fnA; 			// 가능 (반공변성)



타입스크립트 개발자의 중요한 일은, 단순히 변수나 상수나 함수의 타입을 정하는 것이 아니라 그 변수에 프로퍼티가 있을지 없을지, 값의 타입을 받는 것이 한개인지 두개인지, 무작위 타입을 받을 수 있는 지 등의 형태를 잘 분석하여 사용자에게 코드를 문제없이 유연하게 잘 사용할 수 있도록 하는것이 중요하다.

profile
안녕하세요! 26살 프론트엔드 개발자입니다! (2024/03 ~)

0개의 댓글