TypeScript 2.1

keyof 키워드

type alias

// Basic Types Example

type A = string;

const a: A = '스트링';

// Custom Types Example

interface Person {
    name: string;
}

type B = Person;

const b: B = {
    name: 'mark'
};

type alias & keyof keyword

// Basic Types Example

type A = keyof string;

const a: A = 'toString'; // string 의 프로퍼티 이름들만 값으로 올수 있는 타입

// Custom Types Example

interface Person {
    name: string;
    age: number;
}

type B = keyof Person;

const b: B = 'name'; // Person 의 프로퍼티 이름 중 하나

keyof

  • 타입 앞에 쓰여서 타입의 키 이름들만 값으로 가질수 있는 타입을 만들어 내는 키워드
  • 스트링 타입의 서브셋
  • 그래서 뭐 어디 쓰라고 ?

Lookup Type System (1) - before

interface Person {
    name: string;
    age: number; 
}

function getProperty(obj, key) {
    return obj[key];
}

function setProperty(obj, key, value) {
    obj[key] = value;
}

const person: Person = {
    name: 'mark',
    age: 35
};

const personName: boolean = getProperty(person, 'name');
setProperty(person, 'age', 'mark');

Lookup Type System (2) - after

interface Person {
    name: string;
    age: number;
}

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}

function setProperty<T, K extends keyof T>(obj: T, key: K, value: T[K]): void {
    obj[key] = value;
}

const person: Person = {
    name: 'mark',
    age: 35
};

const test: boolean = getProperty(person, 'name');
setProperty(person, 'age', 'mark');

// error TS2322: Type 'string' is not assignable to type 'boolean'.
// error TS2345: Argument of type '"mark"' is not assignable to parameter of type 'number'.

Mapped Type

Mapped Type

  • Type 을 Map 하여 타입을 만들어 냅니다.
  • 기본적으로 keyof 키워드를 이용합니다.
  • 'keyof 를 통해서 만들어내는 타입'을 type alias 로 만들어냈습니다.

Partial<T>

interface Person {
    name: string;
    age: number;
}

// duplicate
// 이미 구현되어 있습니다.
/*
type Partial<T> = {
    [P in keyof T]?: T[P];
};
*/

type PartialPerson = Partial<Person>;

Readonly<T>

interface Person {
    name: string;
    age: number;
}

// duplicate
// 이미 구현되어 있습니다.
/*
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};
*/

type ReadonlyPerson = Readonly<Person>;

Deffered<T>

interface Person {
    name: string;
    age: number;
}

// 구현되어 있지 않습니다.
type Deferred<T> = {
    [P in keyof T]: Promise<T[P]>;
};

type DeferredPerson = Deferred<Person>;

Pick<T, K>

declare function pick<T, K extends keyof T>(obj: T, ...keys: K[]): Pick<T, K>;

interface Person {
    name: string;
    age: number;
}

const mark = {
    name: 'mark',
    age: 35
};

const onlyNameMark = pick(mark, 'age');

Object Spread & Rest

Object Spread & Rest

  • 2.1에 들어왔다는게 놀랐습니다.
  • react 에서 필수로 쓰는 피쳐
  • ES2017 stage3
  • shallow 복사, 합체, 추출 등에 쓰입니다.
  • Array 의 Spread & Rest 와 비슷하게 동작합니다.

Object Spread & Rest

// shallow copy example
const original = {
    a: 'a',
    b: 'b',
    c: 'c'
};

// const copied = original;
const copied = {...original};

// 합체
const sub1 = {x: 'x', y: 'y'};
const sub2 = {z: 'z'};

const merged = {...sub1, ...sub2};

// 추출
// const {a, obj} = original; // obj = {b: 'b', c: 'c'}
const {a, ...obj} = original; // obj = {b: 'b', c: 'c'}

Downlevel Async Function

async / await

  • 이전에는 사용하려면, compile target 을 'es6' 혹은 'es2015' 로 지정해야 했습니다.
  • 이제는 target 을 'es5', 'es3' 로 지정해도 사용할수 있다고 합니다. => 엄청난 것이죠
  • 다만 target 이 'es5' 이하일때는 컴파일 옵션에 Promise 를 사용할 수 있도록 추가해줘야 합니다.
  • 2.2 부터는 Promise 타입은 es5 라이브러리에 들어가게 되었습니다. 단, 생성은 es2015.promise 가 필요합니다. (@saschanaz 추가)

compile option

{
    "compilerOptions": {
        "target": "es5",
        "lib": ["dom", "es2015.promise", "es5"]
    }
}

tslib

tslib

  • 타입스크립트의 헬퍼 함수를 모듈로 빼준 라이브러리
  • npm i tslib 으로 설치
  • --importHelpers 옵션을 컴파일 설정에 추가합니다.

소스 코드 (.ts)

class Parent {

}

class Child extends Parent {

}

compile 결과 (.js) - before

"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
exports.__esModule = true;
var Parent = (function () {
    function Parent() {
    }
    return Parent;
}());
var Child = (function (_super) {
    __extends(Child, _super);
    function Child() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    return Child;
}(Parent));
exports.Child = Child;

compile 결과 (.js) - after

"use strict";
exports.__esModule = true;
var tslib_1 = require("tslib");
var Parent = (function () {
    function Parent() {
    }
    return Parent;
}());
var Child = (function (_super) {
    tslib_1.__extends(Child, _super);
    function Child() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    return Child;
}(Parent));
exports.Child = Child;

d.ts 없이 import

Untyped import

  • 엄청난것 같지만 비추입니다.
  • 이름도 d.ts 없이 임포트가 아니라, 타입 없이 임포트 입니다.
  • 노드 모듈을 import 할때 타입 정의 파일 없이 사용 가능합니다.
  • 하지만 전부 any 로 취급됩니다.
  • any => 크립토나이트 같은 것
  • --noImplicitAny 플래그 달고 컴파일하면 에러

target 추가

target 추가

  • ES2016 / ES2017 / ESNext
  • 해당 타겟 피쳐 이하는 트랜스파일 하지 않습니다.

any 추론 개선

any 추론 개선

  • --noImplicitAny 를 사용했을 때 any 타입에 대한 강화된 체킹 프로세스가 생겼습니다.
  • any 타입으로 추론하지 않겠다는 의미입니다.
  • any 는 크립토나이트

any example

let x;

// You can still assign anything you want to 'x'.
x = () => 42;

// After that last assignment, TypeScript 2.1 knows that 'x' has type '() => number'.
let y = x();

// Thanks to that, it will now tell you that you can't add a number to a function!
console.log(x + y);
//          ~~~~~
// Error! Operator '+' cannot be applied to types '() => number' and 'number'.

// TypeScript still allows you to assign anything you want to 'x'.
x = "Hello world!";

// But now it also knows that 'x' is a 'string'!
x.toLowerCase();

literal 에 대한 추론 개선

literal 할당

  • const, let 에 대한 타입 강화
  • 보시면서 확인하시겠습니다.

literal => const / let

const c1 = 1;  // Type 1
const c2 = c1;  // Type 1
const c3 = "abc";  // Type "abc"
const c4 = true;  // Type true
const c5 = cond ? 1 : "abc";  // Type 1 | "abc"

let v1 = 1;  // Type number
let v2 = c2;  // Type number
let v3 = c3;  // Type string
let v4 = c4;  // Type boolean
let v5 = c5;  // Type number | string

custom element 를 위한 super 처리

constructor, extends, super

  • 제목과 같은 내용이 개선되었지만 결국엔 커스텀 엘리먼트 지원을 위한 내용입니다.
  • 기존 방식과 지원하고 싶은 내용을 비교해보겠습니다.

피쳐 내용

class Base {
    x: number;
    constructor() {
        // return a new object other than `this`
        return {
            x: 1,
        };
    }
}

class Derived extends Base {
    constructor() {
        super();
        this.x = 2;
    }
}

기존 방식

class CustomElement {
    private _element: HTMLElement;
    constructor() {
        this._element = document.createElement('div');
    }

    ...
}

새로운 방식

class CustomElement extends HTMLElement {
    constructor() {
        super();
    }
}

const e = new CustomElement();

document.body.addChild(e);

하지만,

  • 현재 커스텀 엘리먼트는 드래프트 상태입니다.
  • lib 지원에 대해서 차이가 있습니다. (define, register)
  • 이 스펙은 es6 로 타겟팅 하는 것은 의미가 없습니다.
  • es5 로 타겟팅 했을때 new 에는 아직 문제가 있습니다.
  • 커스텀 엘리먼트에 대한 심화 내용은 후에 다루겠습니다.

es5 돌아가는 코드

class TestElementMaker extends HTMLElement {
    constructor() {
        super();
    }

    public test(): void {
        console.log('테스트');
    }
}

const TestElement = document.registerElement("test-element", TestElementMaker);
const e = new TestElement();
e.test();

tsconfig 상속 지원

tsconfig 상속

  • 뭔 컴파일 설정까지 상속이야~
  • 타겟만 다르게 하고 싶을 경우
  • 모듈 시스템만 다르게 하고 싶을 경우

configs/base.json

{
    "compilerOptions": {
        "noImplicitAny": true,
        "strictNullChecks": true
    }
}

tsconfig.json

{
    "extends": "./configs/base",
    "files": [
        "main.ts",
        "supplemental.ts"
    ]
}

tsconfig.nostrictnull.json

{
    "extends": "./tsconfig",
    "compilerOptions": {
        "strictNullChecks": false
    }
}

--alwaysStrict

'use strict';

  • 원래 import, export 가 들어 있는 파일은 전부 strict mode 입니다.
  • 새삼스럽습니다.
  • 그렇지 않은 파일에도 strict mode 로 코드를 해석하고, 최상단에 'use strict'; 가 쓰여집니다.

TypeScript 2.1 발표자료

By Woongjae Lee

TypeScript 2.1 발표자료

타입스크립트 한국 유저 그룹 기술 세미나 201704

  • 1,426