Into the Typescript 2

wook2·2021년 3월 15일
0

typescript

목록 보기
2/4
post-thumbnail

☑️ Enum

다른 프로그래밍 언어에서 제공하는 enum과 동일하다. 열거형이라고 불리며 특정 값들의 집합을 의미한다. 값의 범위가 일정한 종류로 정해져 있을때 사용하면 유용하다.

  • 숫자형 Enum

기본적으로 0부터 시작하여 1씩 증가한다.


Shoes.Nike에 0이 할당되는것을 볼 수 있다.

수동으로도 할당할 수 있다. adidas에 3을 지정하면, 다음 원소의 vans는 4가 되는것을 볼 수있다.

또한 리버스 매핑이 가능하다. key로 value를 얻을 수 있고, 반대로 value로 key를 얻을 수 있다. 리버스 매핑은 숫자형 Enum에서만 가능하다.

  • 문자형 Enum
    문자형 Enum은 auto-increment는 가능하지 않다. 그렇기 때문에 Enum값 전부를 초기화 해주어야 한다.

자바스크립트의 클래스와 프로토타입

자바나 객체지향 언어를 쓰던 개발자들이 자바스크립트를 쓰며 클래스가 없는 것에 혼란스러워 하였다. ES2015부터 자바스크립트에서 클래스를 제공하지만, 자바스크립트는 본질적으로 프로토타입 기반의 언어이다. 자바스크립트에서 제공하는 class는 함수 오브젝트를 이용한 만든 것 이다.

var o = new Object();
o.[[Prototype]] = Foo.prototype;
Foo.call(o);

프로토타입을 할당하고 call 함수를 통해 생성자를 만드는 방법이다.

function Zombie(name, age){
    this.name = name;
    this.age = age;
}
var zombie = new Zombie('child',1);

class Zombie {
    constructor(name, age){
        this.name = name;
        this.age = age;
    }
}
var zombie = new Zombie('child',1);

클래스로 생성한 객체를 Babel을 통해 컴파일하면 위의 function을 통해 선언한 객체와 동일하다.

  • 타입스크립트에서의 클래스

    클래스의 멤버변수의 타입을 지정할수 있고, private, public도 사용 가능하다. 또한 생성자의 매개변수 또한 타입을 지정 할 수 있다.
    readonly로 된 log는 읽기만 가능하다. 즉 zombie.log = 'abc'; 가 불가능하다.
class Zombie {
    private name : string;
    public age : number;
    readonly log : string;
    constructor(name : string, age : number){
        this.name = name;
        this.age = age;
    }
}
var zombie = new Zombie('child',1);

제네릭

  • 제네릭이란?

제네릭( generic ) 이란 데이터 타입을 일반화 한다는 것을 의미한다. 사용할 내부 데이터 타입을 컴파일시 지정하는 방법이다.
즉 제네릭은 선언 시점이 아니라 생성 시점에 타입을 명시하여 하나의 타입이 아닌 다양한 타입을 사용하도록 한다.

  • 제네릭 사용법

함수의 매개변수에 값을 넣는 것 처럼, 제네릭도 사용할 타입을 사용시점에 넣어준다는 점에서 유사하다.

function getSomething<T>(something: T): T {
    return something;
  }

위의 코드는 함수에 T라는 타입을 실행시 주면, 매개변수와 반환값 모두 T라는 타입을 가진다.

  • 제네릭 사용을 통한 중복제거

하나의 인자를 받아와서 출력하는 함수를 만들때, 아래와 같이 같은 동작을 수행하는 함수지만 제네릭을 사용해 중복을 제거할 수 있다.

function getString(something: string): string {
    return something;
  }
function getNumber(something : number) : number {
    return something;
}
getSomething<string>('hello')
getSomething<number>(3)

이처럼 제네릭은 하나의 함수에 여러가지 방법으로 호출할 수 있다.

  • 유니온 사용시 단점
    아래의 코드와 같이 유니온을 사용한다면 a에는 split이라는 함수를 사용할 수 없다.
    string|number 타입에는 split이 없기 때문이다.
function getSomething(something : string | number) {
    console.log(something)
    return something
}
const a = getSomething('abc');
a.split('')

  • 인터페이스에 제네릭 사용
    interface가 사용되는 시점에 타입을 넣어주는 제너릭을 사용할 수 있다.
interface Something<T>{
    value : T;
    name : string
}
const a : Something<number> = {value : '1', name : 'wook'};

number를 받아와야하지만 string을 받았기 때문에 에러를 표시한다.

  • 제네릭 타입 제한
    제네릭으로 선언한 함수의 경우 text는 T라는 나중에 받아올 타입에 대해서 length를 구하는데, 타입스크립트에서는 T라는 타입은 length가 없다고 추론한다.
function logText<T>(text: T): T {
    console.log(text.length); 
    return text;
  }

인터페이스 상속

interface LengthWise{
    length : number;
}

function logText<T extends LengthWise>(text: T): T {
    console.log(text.length); 
    return text;
  }

T라는 타입이 구체적으로 명시가 되있지 않았었기 때문에, 타입을 정의하지 않고 length속성을 허용하기 위해 쓰는 방식이다.

  • keyof
    인터페이스의 키값만 타입으로 받을 수 있다.
interface Shoppingitem {
    name :  string;
    price : number;
    stock : number;
}

function getShoppingItemOption <T extends keyof Shoppingitem> (itmeOption: T) : T {
    return itmeOption
}
getShoppingItemOption('name')


Promise를 이용한 함수타입 정의

Promise값을 받는 함수의 경우, 타입스크립트에서는 reslove전까지 프로미스로 받아오는 값의 타입을 알 수 없다.
그렇기 때문에 Promise로 타입을 지정해주는 것에 더해서 resolve값도 타입을 주어야 한다.

function fetchZombie() : Promise<string[]>{
  let zombies = ['a','b','c'];
  return new Promise(function(resolve){
      setTimeout(() => resolve(zombies),1)
  })
}
fetchZombie()


resolve의 값은 string배열 이기 때문에, Promsise안에 제네릭으로 string배열을 작성한다.


타입 추론

타입 추론이란 타입스크립트가 타입을 추론해 나가는 것이다. 타입 추론에 대해 알기전에 타입스크립트의 지향점에 대해 알아보자.

  • 타입스크립트의 지향점
    타입스크립트 공식문서의 문장을 인용했다.

    One of TypeScript’s core principles is that type checking focuses on the shape that values have. This is sometimes called “duck typing” or “structural subtyping”.

    타입스크립트에서 타입의 체크는 값이 아니라 값의 형태에 초점을 맞춘다. 이를 duck Typing이라고 부른다.

    • duck Typing

      "If it walks like a duck and it quacks like a duck, then it must be a duck"

      덕 타이핑은 동적 타이핑의 한 종류로, 객체 자신이 어떠한 타입인지가 중요하지 않고, 특정 메소드나 속성의 존재로 타입을 판단한다.
      그렇기 때문에 타입의 제약 없이 사용하여 보다 유연하게 타입을 정의할 수 있다.

  • 최적 공통 타입 방식
    일반적인 예로는 배열의 원소 타입을 추론하는 방식이다.

let x = [0, 1, null];

배열의 원소에 number와 null이 있기 때문에 타입스크립트는 number|null 을 원소로 가지는 배열이라고 추론한다.

  • 문맥상 추론
    위의 예제에서는 오른쪽 배열을 통해 타입을 추론하였었다. 하지만 문맥상 추론은 반대방향도 지원한다. 예를 들어 window.onmousedown이라는 이벤트의 콜백으로 mouseEvent를 받는 상황에서, 타입스크립트는 mouseEvent가 window.onmousedown의 콜백으로부터 나온 매개변수 임을 문맥상 추론한다.
window.onmousedown = function(mouseEvent) {
  console.log(mouseEvent.button);   //<- OK
  console.log(mouseEvent.kangaroo); //<- Error!
};

타입단언

타입스크립트가 타입을 다르게 추론하거나 보수적으로 추론하는 경우에 프로그래머가 수동으로 컴파일러에게 타입을 알려주는 것이다.

document.querySelector로 받아온 div는 프로그래머는 해당 태그가 존재한다면 div 변수는HtmlDivElement라고 알 수 있다.

let div = document.querySelector('#box');
div.innerHTML


하지만 위의 사진과 같이 예상과 다른 타입으로 추론을 한다.
그렇기 때문에 프로그래머가 미리 해당 타입에 대해서 단언해 주는 것이 타입단언이다.

let div = document.querySelector('#box') as HTMLDivElement;
div.innerHTML

타입가드

타입가드는 타입단언을 좀더 논리적으로 사용하는 방법이다.
위의 타입단언은 dom객체를 통해 div타입임을 확정하였다. 하지만 이러한 방식은 런타임에서만 알 수 있는 방식이고, TS컴파일러는 알지 못한다.
타입가드는 런타임에서의 타입체크를 컴파일타임에서 알수 있게 해준다.
타입검사를 하는 메서드의 반환값을 변수 is 타입의 형식으로 작성한다.

class Customer {
    name : string;
    address : string;
    isVip() : this is Vip {
        return this instanceof Vip
    }
    isNormal(): this is Normal{
        return this instanceof Normal
    } 
}
class Vip extends Customer {
    doValetParking(){}
}
class Normal extends Customer{
    makeOrder(){}
}

function process(customer : Customer){
    if(customer.isVip()){
        customer.doValetParking();
    }
}

위의 process함수를 만약 타입단언을 이용해 만들었다면,

function process(customer : Customer){
    if(customer.isVip()){
      	(customer as Vip).doValetParking();
    }
}

이러한 방식으로 계속 단언을 해주어야 한다는 불편함이 있다.


타입 호환

  • 타입 호환이란?
    타입스크립트에서 어떤 타입이 특정 타입과 맞는지, 즉 잘 호환되는지 확인하는것

  • 함수 호환
    아래의 예시와 같이 Developer에서 선언된 함수 hello는 age라는 매개변수가 없이도 작동한다. 오른쪽에 있는 타입이 왼쪽보다 구조적으로 더 크다면, 호환 가능하다.
interface Person {
  hello(name: string, age: number) : {introduce : string};
}
class Developer implements Person{
  static age: number = 0;
  hello(name: string){
      let introduce = { introduce : `Hello my name is ${name}`, age : Developer.age};
      return introduce
  }
}

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

forEach의 콜백함수도 기본적으로 3개의 인자를 받지만 value값만 써도 문제가 없는것과 같은 원리이다.

  • 객체 호환
    더 많은 속성을 가지고 있는 객체를 대입하는 것은 가능하지만, 그 반대는 가능하지 않다.
interface x{
  name : string;
}
interface y{
  name : string;
  age: number;
}
let obj1 : x = {name : 'Alice'};
let obj2 : y = {name : 'Bob', age : 7};
obj1 = obj2;
obj2 = obj1;


profile
꾸준히 공부하자

0개의 댓글