2019년 11월 5일 3.7이 정식버전으로 릴리즈되었습니다. 아래에선 3.7에 추가된 주요기능들을 살펴보실 수 있어요! (rc버전에서 작성되었던 내용이지만 설명드린 내용들은 정식버전과 차이가 없어 내용은 수정되지 않았습니다)

해당 포스트는 MicroSoft devblog에 Daniel Rosenwasser님이 기재하신 "Announcing TypeScript 3.7 RC (링크)"를 의역 및 요약해 작성되었습니다. 자세한 내용이 알고 싶으시다면, 해당 포스트보단 링크를 통해 확인해주세요.

설정하기

작성일 기준이기 때문에 포스트보단 사용하시는 IDE나 라이브러리의 이슈등을 확인하시는게 더 정확합니다.

  • 프로젝트의 Typescript버전을 업그레이드 해주세요.

VSCode

  1. VSCode는 1.40버전을 기준으로 3.7버전을 인식, 지원합니다.
  2. 이 버전보다 현재 자신이 사용하는 VSCode버전이 낮다면, 업데이트 완료 후 프로젝트의 Typescript버전을 업그레이드 해주세요.
  3. Ctrl + Shift + P(맥은 Command + Shift + P)를 누르시고, "Typescript"를 입력하시어 Typescript: Select Typescript Version...이라는 옵션을 통해 현재 워크스페이스의 버전이 업그레이드한 버전과 일치하는지 확인해주세요.

eslint + prettier

  • 한국시각으로 11월 9일 prettier 1.19.1버전이 릴리즈되어 prettier의 Typescript 3.7버전 지원이 시작되었습니다.
  • @typescript-eslint/parser, @typescript-eslint/eslint-plugin2.7.0버전을 기준으로 사용하시면 안정적으로 Typescript 3.7버전에 대한 지원을 받을 수 있습니다.

👉 Optional Chaining

Kotlin이나 Swift등의 언어를 사용해보신 분이라면 익숙하실 문법으로, 기존 자바스크립트에선 정식문법으로 채택되지는 않아 @babel/plugin-proposal-optional-chaining라는 바벨플러그인을 통해 Optional Chaining문법을 사용할 수 있었습니다. 하지만 타입스크립트는 이를 지원하지 않아 코드가 길어지는 불편함이 있었는데요.
이번 3.7버전을 통해 타입스크립트에서도 Optional Chaining을 지원한다고 합니다.

Optional Chaining : 포스트를 작성하는 현재 TC39 Stage3 에 속해있는 문법입니다.
포스트를 읽고 계신 지금 Optional Chaining이 어디 속해있는지 궁금하시다면 여기를 클릭해주세요

서버로부터 유저 정보를 받고 싶은 상황을 가정해볼게요.
서버가 응답을 잘해서 데이터를 받았다면 data라는 오브젝트에 user: { name: 'kim' }를 받아올 예정입니다. 하지만 data는 서버나 혹은 클라이언트의 네트워크 상태등 다양한 조건에 의해undefined일 수도 null일 수도 있다고 가정합시다.

위의 조건을 알고 data.user.name를 받게되었을 때, 우리는 다음과 같은 코드를 작성하게 됩니다.

let username = (data === null || data === undefined) ?
    undefined :
    data.user.name();

물론 위의 코드처럼 길지 않게 if삼항 조건 연산자(Conditional operator) 혹은 &&를 이용할수도 있겠죠? 하지만 usernull이나 undefined일 수 있다면? 코드길이나 복잡도는 계속 올라갈겁니다. 보는 개발자가 힘들어지는 것이죠.

Optional Chaining 문법은 위의 코드를 다음과 같이 사용할 수 있게 만들어 줍니다.

let x = data?.user.name();

usernull이나 undefined일 수 있다면?

let x = data?.user?.name();

코드가 훨씬 간단해졌죠?

특징 1. 반환 값

?. 연산자는 좌측의 값에 대해 null인지 undefined인지 확인하고 해당한다면 이후 속성을 찾는 작업은 중지하고 undefined를 반환합니다.

특징 2. &&와의 차이점

?.연산자는 기존 코드에서 많이 사용된 &&를 대체할 수 있겠지만 둘은 엄밀히 체크하는 타입이 다릅니다.
&&연산자는 앞의 피연산자가 falsy(false, null, undefined, '', 0, NaN)인지 확인하지만 ?.연산자는 nullundefined만을 확인합니다.

특징 4. Optional 요소에의 접근

function getFirstElement<T>(arr: T[]) {
  return arr?.[0]
}

위와 같이 arr은 Optional이지만 간단하게 내부 요소에 접근하는 코드를 작성할 수 있습니다.

특징 3. Optional 함수 실행

함수에 대해서도 함수가 있는지 없는지에 따라 실행 혹은 undefined반환하는 용도로 사용할 수 있습니다.

function makeLog(log?:(message: string) => void) {
  return log?.('log가 null이나 undefined가 아니라면 실행될 거예요!')
}

👉 Nullish Coalescing Operator

Nullish Coalescing Operator(이하 NCO)역시 현재 TC39 Stage3에 속해있는 문법입니다.
이 역시 JS에선 babel플러그인인 @babel/plugin-proposal-nullish-coalescing-operator 적용을 통해 사용할 수 있었는데요. 마찬가지로 이번 Typescript 3.7에 포함될 예정입니다.

NCO도 Optional Chaining과 마찬가지로 ??라는 연산자를 통해 앞의 피연산자가 null 혹은 undefined인지 확인하며,

그 쓰임은 ||와 같습니다.

특징 1. ||와의 차이점

이미 ||가 있는데 왜 굳이 ??라는 연산자를 추가한 걸까요?
그 이유는 다음에서 확인할 수 있습니다.

const zero = 0;
const one = 1;
const anyNumber = zero || one; // 1
const anyNumberWithNCO = zero ?? one; // 0

이전의 타입스크립트에서 anyNumber를 사용하는 목적이 아무 숫자나 받길 원하는 변수였다면, ||를 사용해 해결할 수 없었습니다. ||연산자는 falsy값을 확인하기 때문에 anyNumber의 값은 1이 되었기 때문이죠. 이러한 문제들을 보다 간단한 문법을 통해 해결하기 위해 NCO는 도입되었습니다.

👉 (더 많은) 순환 Type Alias

재귀적으로 참조하게 되는 경우에 있어 Type Alias는 태생적으로 제약이 있을 수 밖에 없습니다.
제약없이 작성된 다음과 같은 코드는 당연히 컴파일러에 의해 거부될겁니다.

type Foo = Foo;

하지만 3.6이전의 버전에서 몇몇 순환 Type Alias는 문제가 없음에도 불구하고 Error를 발생시켰습니다.

type ValueOrArray<T> = T | Array<ValueOrArray<T>>;
//   ~~~~~~~~~~~~
// error: Type alias 'ValueOrArray' circularly references itself.

따라서 다음과 같이 interface를 이용해 사용해야만 했습니다.

type ValueOrArray<T> = T | ArrayOfValueOrArray<T>;
interface ArrayOfValueOrArray<T> extends Array<ValueOrArray<T>> {}

하지만 3.7에선 위의 타입선언이 허용됩니다.

예시) JSON 타입지정

JSON을 타입지정하는 예시로 어떻게 타입선언이 변경되는지 확인해볼까요?

before 3.7

type Json =
    | string
    | number
    | boolean
    | null
    | JsonObject
    | JsonArray;

interface JsonObject {
    [property: string]: Json;
}

interface JsonArray extends Array<Json> {}

after 3.7

type Json =
    | string
    | number
    | boolean
    | null
    | { [property: string]: Json }
    | Json[];

👉 호출하지 않는 함수 검사

앞으론 호출하지 않는 함수에 대해서 강력하게 검사합니다. 설명으로는 감이 안오실 수 있으니 코드를 살펴보죠.

interface User {
    isAdmin(): boolean;
}

function doAdminThing(user: User) {
    // 언제나 true를 반환
    if (user.isAdmin) {
        return 'hello'
    }
}

이경우 user.isAdmin을 통해 체크하는 if문은 항상 true를 반환하겠죠. 이전의 typescript는 이를 체크하지 않았지만 이젠 이런식으로 호출하지 않는 함수에 대해서 에러를 표시합니다.

다음과 같은 경우는 에러를 표시하지 않습니다.

interface User {
    notify(): void;
    doNotDisturb?(): boolean;
}

function issueNotification(user: User) {
      // 1. 함수가 Optional인 경우
    if (user.doNotDisturb) { ... }
      // 2. 함수가 있는지 체크하고 함수를 호출한 경우
    if (user.notify) user.notify();
}

만약 user.isAdmin이 있는 함수인지 알고싶었다고 한다면? user.isAdmin타입 정의시에 null혹은 undefined를 포함해 정의하거나 !!user.isAdmin을 사용하면 됩니다.

마무리

앞서 소개드린 내용 외에도 Node.js의 assert모듈과 같은 기능을 하기 위해 추가된 asserts를 통해 assertion function을 작성할 수 있게 되었고,
Javascript파일에서만 사용되던 // @ts-nocheck 주석을 Typescript에서도 사용할 수 있게 되는 등의 크고작은 추가들 역시 있을 예정입니다.

보다 자세하게 알고 싶으시다면 포스트 하단 Reference를 참조해주세요

해당 포스트는 현재(2019.11.01 기준) rc버전에 대해서 포스팅하며, 정식 Release에선 바뀔 수 있다는 점을 말씀드립니다.
(시간이 된다면 릴리즈 후에 수정하도록 하겠습니다.)

Reference

오탈자 및 잘못된 내용에 대해선 댓글로 알려주시면 반영하도록 하겠습니다.
감사합니다 😀