TypeScript 3rd

2woongjae@gmail.com

TypeScript 3rd

2woongjae@gmail.com

Chapter 8. Generic

any => generic

function helloString(message: string): string {
    return message;
}

function helloNumber(message: number): number {
    return message;
}

// 더 많은 반복된 함수들 ...

function hello(message: any): any {
    return message;
}

function helloGeneric<T>(message: T): T {
    return message;
}

console.log(hello('Mark').length);
console.log(hello(35).length);

console.log(helloGeneric(35).toString()); // console.log(helloGeneric<number>(35).toString());

// hello 의 리턴이 any 이기 때문에 타입 헬퍼가 제대로 되지 않음
// helloGeneric 을 사용하면 정상적으로 사용가능

basic generic

function helloGeneric<T>(message: T): T {
    return message;
}

function hello<T>(message: T): T {
    return message;
}

console.log(hello<string>('Hello'));
let age = hello(35);
hello<number>('35');

/*

1. Generic 타입을 쓰지 않으면, T 로 추론
2. Generic 타입을 쓰면, T 를 확인

*/

Generic Array

function hello<T>(messages: T[]): T {
    return messages[0];
}

console.log(hello<string>(['Hello', 'World']));

/*

hello 함수의 제네릭 타입을 [] 를 이용하여 배열로 사용할 수 있음

*/

Generic Types

type HelloGeneric = <T>(message: T) => T;

const hello: HelloGeneric = <T>(message: T): T => {
    return message;
}

console.log(hello<string>('Hello').length);

/*

구현체에 return T 를 설정하지 않아도,
return false 시 오류가 나지 않는다?

*/

Generic Class

class Person<T> {
    private _name: T;
    private _age: number;

    constructor(name: T) {
        this._name = name;
    }
}

new Person('Mark');
// new Person<string>(35);

/*

명시적으로 제네릭 타입을 설정하면 오류

*/

Generic with extends

class Person<T extends string | number> {
    private _name: T;
    private _age: T;

    constructor(name: T) {
        this._name = name;
    }
}

new Person('Mark');
new Person(35);
// new Person(true);

/*

T 가 string 또는 number 를 상속받기 때문에 boolean 은 안된다.

*/

Generic with multiple types

class Person<T, K> {
    private _name: T;
    private _age: K;

    constructor(name: T, age: K) {
        this._name = name;
        this._age = age;
    }
}

new Person('Mark', 35);

type lookup system

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

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

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;
}

console.log(getProperty(person, 'name'));
// console.log(getProperty(person, fullname));
setProperty(person, 'name', 'Anna');
console.log(getProperty(person, 'name'));
// setProperty(person, 'name', 24);

Chapter 9. iterator

for..of

  • es3
    • for (var i = 0; i < array.length; i++)
  • es5
    • array.forEach
      • return 으로 순회를 탈출할 수 없다.
  • es6
    • for (const item of array)
      • 배열에서만 사용이 가능

for..in

  • 배열을 순회할 때는 사용하지 않을 것
    • index 가 number 가 아니라 string 으로 나온다.
    • 배열의 프로퍼티를 순회할 수도 있다.
    • prototype 체인의 프로퍼티를 순회할 수도 있다.
    • 루프가 무작위로 순회할 수도 있다.
    • for..of 를 쓸 것
  • 객체를 순회할 때
    • for (const prop of Object.keys(obj)) 도 사용할 수 있다.

Example

const array = ['first', 'second'];
const obj = {
    name: 'Mark',
    age: 35
};

// 배열에 for..of 이용
for (const item of array) {
    console.log(typeof item + ', ' + item);
}

// 배열에 for..in 이용
// item 이 string 타입의 숫자
for (const item in array) {
    console.log(typeof item + ', ' + item);
}


// 객체에 for..of 이용 => 오류
/*
for (const item of obj) {
    console.log(typeof item + ', ' + item);
}
*/

// 객체에 for..in 이용
for (const item in obj) {
    console.log(typeof item + ', ' + item);
}

// 객체의 keys 들에 for..of 이용
for (const item of Object.keys(obj)) {
    console.log(typeof item + ', ' + item);
}

target es3 forEach

const array = ['first', 'second'];

// ts
array.forEach((item) => {
    console.log(item);
});

// js
array.forEach(function (item) {
    console.log(item);
});

/*

target 이 es3 인데도 forEach 는 트랜스파일이 되지 않았음.
https://github.com/Microsoft/TypeScript/issues/2410

*/

Symbol.iterator

  • 프로퍼티이며, 함수가 구현되어있으면, iterable 이라고 한다.
  • Array, Map, Set, String, Int32Array, Uint32Array, etc. 에는 내장된 구현체가 있으므로 이터러블 하다.
  • 그냥 객체는 이터러블하지 않다.
  • 이터레이터를 통해 이터러블한 객체의 Symbol.iterator 함수를 호출한다.

Symbol.iterator

  • target : es3 or es5
    • Array 에만 for..of 사용 가능
    • 일반 객체에 사용하면 오류
  • target : es6
    • Symbol.iterator 함수를 구현하면 어떤 객체에도 for..of 사용 가능

lib.es6.d.ts

interface IteratorResult<T> {
    done: boolean;
    value: T;
}

interface Iterator<T> {
    next(value?: any): IteratorResult<T>;
    return?(value?: any): IteratorResult<T>;
    throw?(e?: any): IteratorResult<T>;
}

interface Iterable<T> {
    [Symbol.iterator](): Iterator<T>;
}

interface IterableIterator<T> extends Iterator<T> {
    [Symbol.iterator](): IterableIterator<T>;
}

Custom Iterable

class CustomIterable implements Iterable<string> {
    private _array: Array<string> = ['first', 'second'];

    [Symbol.iterator]() {
        var nextIndex = 0;

        return {
            next: () => {
                return {
                    value: this._array[nextIndex++],
                    done: nextIndex > this._array.length
                }
            }
        }
    }
}

const cIterable = new CustomIterable();

for (const item of cIterable) {
    console.log(item);
}

Chapter 10. decorator

Decorator 종류

  • Class Decorator
  • Method Decorator
  • Property Decorator
  • Parameter Decorator

Decorator 코드 작성 준비

  • step1. 프로젝트 생성
    • mkdir ts-decorator
    • cd ts-decorator
    • yarn init -y
  • step2. typescript 설치
    • yarn add typescript -D
  • step3. tsconfig 설정
    • node_modules/.bin/tsc --init
    • experimentalDecorators 추가

  • ​step4. vscode 컴파일 설정

    • ${workspaceRoot}/node_modules/.bin/tsc

    • command + shift + <B>

Class Decorator Basic

function hello(constructorFn: Function) {
    console.log(constructorFn);
}

function helloFactory(show: boolean) {
    if (show) {
        return hello;
    } else {
        return null;
    }
}

// @hello
@helloFactory(true)
class Person {
    constructor() {
        console.log('new Person()');
    }
}

new Person();

/*

helloFactory 는 팩토리 스타일

*/

Class Decorator Advanced

function addHello(constructorFn: Function) {
    constructorFn.prototype.hello = function() {
        console.log('hello');
    }
}

@addHello
class Person {
    constructor() {
        console.log('new Person()');
    }
}

const person = new Person();
(<any>person).hello();

Method Decorator

function editable(canBeEdit: boolean) {

    return function(target: any, propName: string, description: PropertyDescriptor) {
        console.log(canBeEdit);
        console.log(target);
        console.log(propName);
        console.log(description);
        description.writable = canBeEdit;
    }
}

class Person {
    constructor() {
        console.log('new Person()');
    }

    @editable(true)
    hello() {
        console.log('hello');
    }
}

const person = new Person();
person.hello();
person.hello = function() {
    console.log('world');
}
person.hello();

Property Decorator

function writable(canBeWrite: boolean) {
    return function(target: any, propName: string): any {
        console.log(canBeWrite);
        console.log(target);
        console.log(propName);
        return {
            writable: canBeWrite
        }
    }
}

class Person {
    @writable(false)
    name: string = 'Mark';

    constructor() {
        console.log('new Person()');
    }
}

const person = new Person();
console.log(person.name);

/*

undefined

*/

Parameter Decorator

function printInfo(target: any, methodName: string, paramIndex: number) {
    console.log(target);
    console.log(methodName);
    console.log(paramIndex);
}

class Person {
    private _name: string;
    private _age: number;

    constructor(name: string, @printInfo age: number) {
        this._name = name;
        this._age = age;
    }

    hello(@printInfo message: string) {
        console.log(message);
    }
}

/*

Person { hello: [Function] }
hello
0
[Function: Person]
undefined
1

*/

Chapter 11. Type Inference

타입 추론

  • 기본적으로 타입을 명시적으로 쓰지 않을 때 추론하는 방법에 대한 규칙
    • 명시적으로 쓰는 것은 타입 추론이 아니라 코드를 읽기 좋게 하는 지름길
  • let 은 기본적으로 우리가 아는 기본 자료형으로 추론
  • const 는 리터럴 타입으로 추론
    • 오브젝트 타입을 타입을 쓰지 않으면, 프로퍼티는 let 처럼 추론
      • const person = {name: 'Mark', age: 35}; 면
      • person => {name: string; age: number;} 로 추론
  • 대부분은 추론이 쉽다.
    • 단순 변수
    • structuring, destructuring
  • array, 함수의 리턴에서는 원하는데로 얻기가 힘들다.

배열 타입 추론

const array1 = [];
const array2 = ['a', 'b', 'c'];
const array3 = ['a', 1, false];

class Animal {
    name: string;
}

class Dog extends Animal { 
    dog: string;
}

class Cat extends Animal {
    cat: string;
}

const array4 = [new Dog(), new Cat()];

리턴 타입 추론

function hello(message: string | number) {
    if (message === 'world') {
        return 'world';
    } else {
        return 0;
    }
}

유니온 타입과 타입 가드

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

interface Car {
    brand: string;
    wheel: number;
}

function isPerson(arg: any): arg is Person {
    return arg.name !== undefined;
}

function hello(arg: Person | Car) {
    if (isPerson(arg)) {
        console.log(arg.name);
        // console.log(arg.brand);
    } else {
        // console.log(arg.name);
        console.log(arg.brand);
    }
}

Chapter 12. React with TypeScript

https://github.com/saystone/tic-tac-toe

TypeScript 기초 강의 (3)

By Woongjae Lee

TypeScript 기초 강의 (3)

타입스크립트 한국 유저 그룹 기초 스터디 201705

  • 1,821