[이펙티브 타입스크립트] 1장 타입스크립트 알아보기

Bor·2022년 1월 15일
0

타입스크립트

목록 보기
1/2
post-thumbnail

1장에서는 타입스크립트의 큰 그림들을 이해하는 데 도움이 될 내용을 공부한다!

  • 타입스크립트란?
  • 자바스크립트와의 관계
  • 타입은 null이 가능한지, any 타입에서는 어떨지, duck typing이 가능한지

타입스크립트는 사용 방식 면에서 조금 독특한 언어이다. 인터프리터로 실행되는 것도 ❌, 저수준 언어로 컴파일 되는 것도 ❌ 실행 역시 타입스크립트가 아닌 자바스크립트로 이뤄진다. 그래서 관계가 밀접하고 혼란스러운일이 이로 인해 발생하기도 한다.

아이템1 타입스크립트와 자바스크립트 관계 이해하기

"타입스크립트는 자바스크립트의 상위집합이다" 근데 그게 무슨말? 🙋 자바스크립트 프로그램에 문법 오류가 없다면 유효한 타입스크립트 프로그램. 그런데 문법 오류가 없어도 이슈가 있다면 타입 체커에게 지적당할 가능성이 높다. 그러나 문법의 유효성과 동작의 이슈는 독립적이어서 타입스크립트는 여전히 작성된 코드를 파싱하고 자바스크립트로 변환가능

자바스크립트 파일이 .js, jsx 확장자를 사용하는 반면 타입스크립트는 .ts, .tsx 를 사용한다. 상위 집합이므로 .js파일에 있는 코드는 이미 타입스크립트라고 할 수 있다. 이러한 특성으로 인해서 ⭐ 기존에 존재하는 자바스크립트 코드를 타입스크립트로 migration 하는데 엄청난 이점 ⭐. 기존 코드를 그대로 유지하면서 일부만 타입스크립트 적용이 가능하기 때문에.

타입스크립트 프로그램이지만 자바스크립트가 아닌 프로그램이 존재. 이는 타입스크립트가 타입을 명시하는 추가적인 문법을 가지기 때문. 타입스크립트 컴파일러는 타입스크립트 뿐만 아니라 일반 자바스크립트 프로그램에도 유용.

: string은 타입스크립트에서 쓰이는 타입 구문. 타입 구문을 사용하는 순간 자바스크립트는 타입스크립트 영역으로 들어가게 된다.

타입시스템의 목표 중 하나는 런타임에서 오류를 발생시킬 코드를 미리 찾아 내는 것. 타입스크립트가 정적 타입 시스템이라는 것은 바로 이런 특징. 그러나 타입 체커가 모든 오류를 찾아내지는 않는다.

위와 같은 코드는 유효한 자바스크립트(또한 타입스크립트)이며 어떠한 오류도 없이 실행된다. 그러나 state.capitol은 의도한 코드가 아니다. 이러한 경우에 타입스크립트는 추가적인 타입 구문 없이도 오류를 찾아낸다.

타입스크립트는 타입 구문 없이도 오류를 잡을 수 있지만, 타입 구문을 추가하면 훨씬 더 많은 오류를 찾을 수 있다. 이는 의도가 무엇인지 타입 구문을 통해 알려줄 수 있기 때문에.

  • 객체리터럴은 알려진 속성만 지정할 수 있지만~ 과 같은 오류가 나온다.

  • 이제 오류가 어디에서 발생했는지 찾을 수 있고 제시된 해결책도 올바르다.

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

  • 타입스크립트는 자바스크립트 런타임 동작을 모델링하는 타입 시스템을 가지고 있기 때문에 런타임 오류를 발생시키는 코드를 찾아내려 한다. 그러나 모든 오류를 찾아내리라 기대하면 안 된다. 타입 체커를 통과하면서도 런타임 오류를 발생시키는 코드는 충분히 존재할 수 있다.
  • 잘못된 매개변수 개수로 함수를 호출하는 경우처럼, 자바스크립트에는 허용되지만 타입스크립틍서는 문제가 되는 경우도 있다.

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

현재 시점에는 설정이 거의 100개에 이른다. 이 설정은 커맨드 라인을 사용해 할 수도 있고 tsconfig.json 설정 파일을 통해서도 가능하다.

가급적 설정파일을 이용하는 것이 좋다. 그래야 동료들이나 다른 도구들도 알 수 있기 때문. 타입스크립트는 어떻게 설정하느냐에 따라서 완전히 다른 언어처럼 느껴질 수도 있다. 설정을 제대로 사용하려면 noImplicityAny와 strictNullChecks를 이해해야한다.


⛳ noImplicityAny

변수들이 미리 정의된 타입을 가져야 하는지 여부를 제어한다. any 타입을 매개변수에 이용하면 타입 체커는 쓸모 없어진다. any는 유용하지만 매우 주의해서 사용해야한다.

  • any를 코드에 넣지 않았지만 any 타입으로 간주되기에 이를 '암시적 any'라고 부른다. 그런데 같은 코드임에도 noImplicitAny를 설정하니 오류가 된다. 🙋 이러한 오류들은 명시적으로 :any라고 선언해주거나 더 분명한 타입을 사용하면 해결할 수 있다.

  • 타입스크립트는 타입 정보를 가질 때 가장 효과적이므로, 되도록이면 noImplicitAny를 설정해야 한다. 처음부터 요것을 설정해 코드를 작성할 때마다 타입을 명시하도록 하면 문제 발견도 수월하고 가독성이 좋아지면 생산성도 좋아진다.
  • noImplicityAny 설정 해제는 자바스크립트로 되어 있는 기존 프로젝트를 타입 스크립트로 전환하는 상황에만 필요하다.

⛳ strictNullChecks

null과 undefined가 모든 타입에서 허용되는지 확인하는 설정.

  • 위 코드는 strictNullChecks가 해제되었을 때는 유효한 코드이지만 설정 후에는 오류가 난다. null대신 undefined를 써도같은 오류가 난다. 만약 null을 허용하려고 한다면, 의도를 명시적으로 드러내서 오류를 고칠 수 있다.

  • 만약 null을 허용하지 않으려면, 이 값이 어디서부터 왔는지 찾아야 하고 null을 체크하는 코드나 단언문을 추가해야 한다.

  • strictNullChecks는 null과 undefined 관련된 오류는 잡아내는데 많은 도움이 되지만, 코드 작성을 어렵게 하기도. 새 프로젝트를 시작한다면 가급적 사용하는 것이 좋지만 처음이거나 자바스크립트를 마이그레이션 중이라면 설정하지 않아도 괜찮다. 또한 strictNullChecks를 설정할면 noImplicityAny를 먼저 설정해야 한다.

  • 언어에 의미적으로 영향을 미치는 설정들이 많지만 이 모든 체크를 설정하고 싶다면 strict 설정을 하면 된다. 타입스크립트에 strict 설정을 하면 대부분의 오류를 잡아준다.


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

큰 그림에서 타입스크립트 컴파일러는 두 가지 역할을 수행한다.

  • 최신 타입스크립트/자바스크립트를 브라우저에서 동작할 수 있도록 구버전의 자바스크립트로 트랜스 파일한다.
  • 코드의 타입 오류를 체크한다

트랜스파일 : translate + compile, 소스코드를 동일한 동작을 하는 다른 소스코드(다른 버전, 언어 등)로 변환하는 행위를 의미. 결과물이 여전히 컴파일 되어야 하는 소스코드이기 때문에 컴파일과는 구분해서 부른다.

여기서 놀라운 점은 이 두 가지가 서로 완벽히 독립적. 타입스크립트가 자바스크립트로 변환될 때 코드 내 타입에는 영향을 주지 않으며 실행 시점에도 타입은 영향을 주지 않는다. 그래서 이를 통해 할 수 있는 일과 없는 일을 짐작할 수 있다.


타입 오류가 있는 코드도 컴파일이 가능하다.

컴파일은 타입 체크와 독립적으로 동작하기에 타입 오류가 있는 코드도 컴파일이 가능하다. 타입 체크와 컴파일이 동시에 이뤄지는 C나 자바와 같은 언어와 달리 경고와 비슷해 문제가 될 만한 부문을 알려 주지만 그렇다고 빌드를 멈추지는 않는다.

이를통해 문제 된 오류를 수정하지 않더라도 다른 부분을 테스트할 수 있다. 만약 오류가 있을 때 컴파일 하지 않으려면 tsconfig.json에 noEmitOnError를 설정하거나 빌드 도구에 동일하게 적용하면 된다.


런타임에는 타입 체크가 불가능하다

  • instanceof 체크는 런타임에 일어나지만 , Retangle은 타입이기 때문에 런타임 시점에 아무런 역할을 할 수 없다. 타입스크립트의 타입은 '제거 가능'하다. 실제로 자바스크립트로 컴파일 되는 과정에서 모든 인터페이스, 타입, 타입구문은 제거되어 버린다.
  • 앞의 코드에서 다루고 있는 shape 타입을 명확하게 하려면, 런타임에 타입 정보를 유지하는 방법이 필요하다. 하나의 방법은 height 속성이 존재하는지 체크해보는 것.

  • 속성 체크는 런타입에 접근 가능한 값에 관련되지만, 타입 체커 역시도 shape의 타입을 Rectangle로 보정해주기에 오류가 사라진다.
  • 타입 정보를 유지하는 또 다른 방법으로는 런타임에 접근 가능한 타입 정보를 명시적으로 저장하는 '태그'기법이 있다.

태그기법

  • 여기서 ShapeOf 타입은 '태그된 유니온'의 한 예이다. 이 기법은 런타임에 타입 정보를 손쉽게 유지할 수 있기에 타입스크립트에서는 흔하게 볼 수 있다.
  • 타입(런타임 접근 불가)와 값(런타임 접근 가능)을 둘 다 사용하는 기법도 있다. 타입을 클래스로 만들면 된다. Square과 Retangle을 클래스로 만들면 오류를 해결할 수 있다.

클래스로 해결하기

인터페이스는 타입으로만 사용 가능하지만 정사각형을 클래스로 선언하면 타입과 값으로 모두 사용할 수 있으므로 오류가 없다. type 모양 = 직사각형 | 정사각형 부분에서 정사각형은 타입으로 참조되지만, shape instanceof 정사각형 부분에서는 값으로 참조된다. ⭐ 이후에 아이템8에서 다룰 어떻게 참조되는지 구분하는 것은 매우 중요하다!


타입 연산은 런타임에 영향을 주지 않는다

string or number 타입인 값을 항상 number로 정제하는 경우를 가정. 다음 코드는 체커를 통과하지만 잘못된 방법을 사용했다.

변환된 자바스크립트 코드를 보면 이 함수가 실제로 어떻게 동작하는지 알 수 있다.

코드에 아무런 정제 과정이 없다. as number는 타입 연산이고 런타임 동작에는 아무런 영향을 미치지 않는다. 값을 정제하기 위해서는 런타임의 타입을 체크해야 하고 자바스크립트 연산을 통해 변환을 수행해야 한다.

  • 요것을 변환하면,

런타임 타입은 선언전 타입과 다를 수 있다.


타입스크립트는 일반적으로 실행되지 못하는 죽은 코드를 찾아내지만, 여기서는 strict를 설정해도 찾아내지 못한다. :boolean이 타입 선언문이라는 것에 주목. 타입스크립트의 타입이기에 :boolean은 런타임에 제거된다. 타입 스크립트에서는 런타입 타입과 선언된 타입이 맞지 않을 수도. 타입이 달라지는 혼란스러운 상황을 가능한 한 피해야 한다. 선언된 타입이 언제든지 달라질 수 있다는 것을 명심!


타입스크립트 타입으로는 함수를 오버로드 할 수 없다


C++과 같은 언어는 동일한 이름에 매개변수만 다른 여러 버전의 함수를 허용. 이를 '함수 오버로딩이'이라고 한다. 그러나 타입스크립트에서는 타입과 런타임의 동작이 무관하므로 함수 오버로딩은 불가. 지원은 하지만 온전히 타입 수준에서만 동작한다. 하나의 함수에 대해서 여러 개의 선언문을 작성할 수 있지만 구현체(implementation)는 오직 하나 뿐.


타입스크립트 타입은 런타임 성능에 영향을 주지 않는다

타입과 타입 연산자는 자바스크립트 변환 시점에 제거가 되기에 런타임의 성능에 아무런 영향을 주지 않는다. 타입스크립트의 정적 타입은 실제로 비용이 전혀 들지 않는다.

  • 런타임 오버헤드가 없는 대신, 타입스크립트 컴파일러는 '빌드타임' 오버헤드가 존재. 타입스크립트 팀은 컴파일러 성능을 매우 중요하게 생각. 따라서 컴파일은 일반적으로 빠르며 특히 증분(incremental)빌드 시에 더욱 체감된다. 오버헤드가 커지면 빌드 도구에서 '트랜스파일만(transpile)'을 설정해 타입 체크를 건너뛸 수 있다.

  • 타입스크립트가 컴파일하는 코드는 오래된 런타임 환경 지원을 위해 호환성을 노피이고 성능 오버헤드를 감안할 지, 호환성을 포기하고 성능 중심의 네이티브 구현체를 선택할 지 문데에 맞닥뜨릴 수 있다. 예를 들어 제너레이터 함수가 ES5 타깃으로 컴파일 되려면, 타임스크립트 컴파일러는 호환성을 위해 특정 헬처 코드를 추가할 것. 이런 경우가 제너레이터의 호환성을 위해 오버헤드 또는 성능을 위한 네이티브 구현체 선택의 문제.


TIL

  • 완전히 다른 언어가 아니었구나! 그리고 인터프리터로 실행되거나 저수준 언어로 컴파일되지 않고 JS로 실행되는 것도 신기하다. 그래서 오히려 널리 쓰이는구나.
  • 오 신기하게 대부분의 설정이 주석처리되어 있지만 tsconfig.json에 이미 존재한다. 필요한 것만 옆에 설명을 읽고 주석을 헤제하면 되는건가?
  • ㅎㅎ 이 책을 읽을 수준이 아직 아님을 깨달았다! 타입스크립트 프로그래밍을 보고 나중에 ts에 적응을 하면 이 책을 봐야지! 딥다이브를 처음볼 때랑 비교해보면 자기 수준을 알면서 책을 보는 것도 성장했다!

0개의 댓글