bkdragon's log

1장 타입스크립트 알아보기 본문

이펙티브 타입스크립트

1장 타입스크립트 알아보기

bkdragon 2024. 10. 23. 20:18

타입스크립트와 자바스크립트의 관계 이해하기

타입스크립트는 문법적으로 자바스크립트의 상위집합이다. 자바스크립트 프로그램에 문법 오류가 없다면, 유효한 타입스크립트 프로그램이다. 이 특성은 자바스크립트 코드를 타입스크립트로 마이그레이션하는 데 엄청난 이점이 된다. 모든 자바스크립트 프로그램은 타입스크립트 프로그램이지만 그 반대는 아니다.

타입스크립트의 타입 시스템의 주된 목표는 런타임에 오류를 발생시킬 코드를 미리 찾아내는 것이다. 타입스크립트를 정적 타입 시스템이라하고 하는 것이 바로 이 특징을 말하는 것이다.

타입스크립트는 자바스크립트의 런타임 동작을 모델링한다.

const x = 2 + "3";
const y = "2" + 3;

위 코드는 타입스크립트에서 오류를 발생하지 않는다.

타입스크립트 설정 이해하기

타입스크립트 설정은 tsconfig.json 파일에서 할 수 있다. any를 허용하지 않는 noImplicitAny, 모든 타입에서 null 또는 undefined를 허용하는 strinctNullChecks. 이 두가지가 대표적인 설정이다. 두 설정 모두 켜서 사용하는게 타입 안정성을 가장 높힐 수 있다.

되도록이면 noImplicitAny 는 true 로 설정하는 것이 좋다. 다만, 자바스크립트 프로젝트를 타입스크립트로 마이그레이션 할 때는 false 로 설정하는 것이 훨씬 수월하다.

코드 생성과 타입이 관계없음을 이해하기

타입스크립트가 자바스크립트로 변환될 때 코드 내의 타입과 실행 시점에서의 타입에는 영향을 미치지 않는다.

이로 인해 아래와 같은 특성을 가진다.

  1. 타입 오류가 있는 코드도 컴파일이 가능하다. (noEmitOnError 로 막을 순 있다.)
  2. 런타임에 타입 체크가 불가능하다.
interface Squere {
}
interface Rectangle extends Squere {}

type Shape = Squere | Rectangle

function calculateArea(shape: Shape) {
    if(shape instanceof Retangle) {
    }
}

위 코드는 타입을 값을 사용하고 있다는 에러가 발생한다. 타입 체크를 하고 싶다면 in 키워드로 속성을 확인하거나 태그를 기입하는 방법이 있다.

interface A {
  tag : "A"
}
interface B {
  tag : "B"
}
  1. 타입 연산은 런타임에 영향을 주지 않는다. (ex.as 문이 타입을 변경하거나 하지 않는다.)
  2. 런타임 타입은 선언된 타입과 다를 수 있다.
  3. 타입스크립트 타입으로는 함수를 오버로드할 수 없다.
  4. 타입스크립트 타입은 런타임 성능에 영향을 주지 않는다.

구조적 타이핑 익숙해지기

자바스크립트는 덕 타이핑 기반이다. 타입스크립트는 이를 모델링하기 위해 구조적 타이핑을 사용한다.

interface Vector2D {
    x: number;
    y: number;
}

interface Vector3D {
    x: number;
    y: number;
    z: number;
}

const calculate = (v: Vector2D) => {
    return Math.sqrt(v.x * v.x + v.y * v.y);
};

const normalize = (v: Vector3D) => {
    const length = calculate(v);
    return {
        x: v.x / length,
        y: v.y / length,
        z: v.z / length,
    };
};

normalize({ x: 3, y: 4, z: 5 });

위 코드는 구조적 타이핑으로 인한 오류가 있다. Vector3D 는 Vector2D가 될 수 있기 때문에 calculate의 인자로 사용이 가능하지만 z 속성이 무시된다.

클래스 역시 구조적 타이핑의 규칙을 따른다.

class C {
    foo: string;
    constructor(foo: string) {
        this.foo = foo;
    }
}

const c = new C("foo");

const d: C = {
    foo: "bar",
};

위 코드는 에러가 발생하지 않는다.

any 타입 지양하기

any 타입은 위험하다.

  1. any 타입에는 타입 안전성이 없다. (타입 체크가 동작하지 않는다.)
  2. any 타입은 함수 시그니처를 무시한다. (any 타입 변수가 함수의 매개변수 타입을 무시하고 들어간다.)
  3. any 타입에는 언어 서비스가 적용되지 않는다. (자동완성, 도움말이 제공되지 않는다.)
  4. any 타입은 설계를 감춘다.
  5. any 타입은 타입시스템의 신뢰도를 떨어뜨린다.