Skip to content

Instantly share code, notes, and snippets.

@rabelais88
Last active July 15, 2020 03:43
Show Gist options
  • Save rabelais88/be9ae5cf23005c553135ccc7cc2da09a to your computer and use it in GitHub Desktop.
Save rabelais88/be9ae5cf23005c553135ccc7cc2da09a to your computer and use it in GitHub Desktop.
타입스크립트 실전 압축 스터디

타입스크립트 실전 압축 스터디

  • 링크된 git은 빅픽쳐 인터랙티브 계정에서만 접속 가능합니다.

  • script-only 실습 git

  • nuxt 실습 git - master 브랜치는 해답, study 브랜치에서 실습

  • 타입스크립트는 내용이 방대하지만, 라이브러리를 제작하는 경우를 제외하고 실제로 사용되는 내용은 매우 한정적이므로 이번 스터디는 실전에 많이 사용되는 내용에 한하여 정리합니다.

interface

  • 각종 타입별 타입 사용법에 대한 기초 핸드북은 typescript handbook을 추천합니다.

  • interface를 통하여 object 형태의 argument 만들어보기

interface myIntf {
  a: number;
  b: string;
}

// myFunc({ a: number, b: string }):string 과 동일
function myFunc(arg: myIntf):string { /* ... */ }

// 원소에 대하여 타입을 강제하는 행렬이나, 선택적인 값도 나타낼 수 있다.
interface myIntf {
  removableKeyA?: string; // 생략해도 되지만, 입력될 때에는 반드시 string이어야 한다.
  b: string[]; // ['a','b','c' ...]는 되지만, ['a', 1, 'c' ...]는 안된다
  c: string | number; // string 또는 number
}
  • interface를 통하여 function의 결과값까지 통째로 타입 지정하고, 재활용하기
// argument일 때 일반적으로 arg라는 이름을 많이 사용한다
interface myArg {
  // 인터페이스는 항상 왼쪽이 키, 오른쪽이 타입인데, 괄호는 즉 function이 키로도 사용됨을 나타낸 것임.
  (a: string, b: number): string;
  // 만약 한 개 이상의 내용이 포함되면 override로 인식하여 조건에 맞으면 해당 타입으로 간주
  // (a: string, b: number, c: string): number;
}

const myFunc: myArg = (a, b) => { /* .. */ };

type vs. interface

  • 타입과 인터페이스의 차이를 가장 쉽게 나타내면 다음과 같다: type은 단일 타입이고, interface는 항상 object.
  • 물론 type이 항상 단일 타입인 것은 아니지만, 일반적인 활용 범위에서 type은 단일 타입인 경우가 많다. 또한 typeprimitive type이외에 우리가 만든 interface도 하나의 타입으로 받아들이기 때문에, interface를 다른 타입명으로 재활용하고자 할 때에도 용이하다.
type myType = number;
function myFunc(a: myType): myType { /* ... */ }

interface document {
  id: number;
  title: string;
  author: string;
}
type documents = document[];
type anotherDocu = document;

// 타입에도 or 선택자(|)를 사용할 수 있다. 즉, interface에서 타입으로 사용했던 모든 방식을 type에서도 사용할 수 있다.
// 보통 이러한 type join은 특정 store에 묶여있는 다수의 redux action이나 vuex action등을 정의할 때 용이하다.
type vagueType = number | string;
// 만약 해당 store(reducer)에 아래의 타입을 묶어둘 경우 해당 스토어에서 다른 스토어의 액션을 사용하지 못하도록 타입으로 제한할 수 있다.
type documentStoreActions = getDocumentAct | removeDocumentAct | getDocumentsAct /* ... */;
const documentReducer = (state, action: documentStoreActions) => { /* ... */ };

extends, implements

  • 시간 제한으로 당일 스터디에서 진행하지 못한 내용.
  • extends는 이미 작성한 interface를 확장할 때 사용하고, implementsclass의 형태를 지정할 때 사용한다.
interface documentBase {
  id: number;
  updatedAt: number;
}

interface post extends documentBase {
  author: string;
  title string;
}

// documentBase === { id, updatedAt, author, title }

interface vehicle {
  maxSpeed number;
  drive(speed: number): boolean;
}

class car implements vehicle {
  maxSpeed: number;
  drive(speed: number): boolean {
  }
}

any, generics, type conversion

  • 형태를 알 수 없는 타입을 any로 정의한다.
  • 제네릭(generic)은 특정 기능을 좀더 범용적으로 만들고자 할 때 사용한다. 외부에서 커스텀 타입을 제공할 수 있는 형태다.
  • 형변환(type conversion)은 이미 타입이 지정된 값에 대하여 내가 원하는 방향으로 타입을 재지정하고자 할 때 사용한다.
  • 제네릭에서 정의되지 않은 커스텀 타입에 대해 T라는 이름을 사용하도록 관례가 정해져 있다. 이 이외에 몇가지 관례가 더 있으나, 공식 문서를 참조하자.
function myFunc<T>():T { /* ...이 안에서도 T를 사용할 수 있다. */ };
// Promise 타입도 제네릭을 기본 탑재하고 있다.
const req = await axios.get('...') // req === Promise<any>

// axios의 기능안에 myDocument라는 타입이 없으므로 외부에서 generic또는 type conversion으로 제공한다.
const req = await axios.get('https://mysite/document');
const parsed = <myDocument>req; // any 타입이 myDocument타입으로 type conversion되었다.

namespace

  • namespace는 전역적(global)으로 사용되는 값을 정의하고자 할 때 사용된다. 따라서 모든 코드에 전역적인 영향을 미친다.
  • 실무에서는 보통 이미 라이브러리로 정의된 전역값을 겹쳐쓰기 하고자 할 때 주로 사용한다.
// index.d.ts

// node_modules/vue/types 폴더 안의 vue.d.ts를 겹쳐쓰기한다.
// 이렇게 작업할 경우 Vue 인스턴스 내부에서 $myExtra를 사용할 수 있게 된다.
// this.$myExtra()
declare module 'vue/types/vue' {
  interface Vue {
    $myExtra: typeof myExtraFunc
  }
}

실전 팁

  • 타입 파일은 항상 별도의 디렉토리에 별도의 타입 전용 파일(*.d.ts)형태로 관리하는 것이 좋다. 왜냐면 라이브러리는 import하지 않더라도 타입만 import하여 순환참조(circular-dependency)로 인한 오류를 막으면서, 해당 자료의 형태는 미리 알 수 있는 장점이 있다.
  • 만약 사용하는 라이브러리에 타입스크립트가 없다면, Definitely Typed 프로젝트를 활용하자. 현존하는 대부분의 라이브러리에 타입을 추가해주는 오픈소스 프로젝트로 상당수의 라이브러리를 커버하고 있다고 한다. 물론 아주 자잘한 라이브러리는 커버하지 못하므로, 이런 경우에는 강제 형변환을 이용한다. 일반적으로 @types/...형태로 관리되고 있다. 또한 Definitely Typed를 이용하기 전에, 스택오버플로우나 공식 문서의 타입지원 부분을 꼭 확인해보자. 타입을 제공하더라도 원래 지정된 방식으로 사용하지 않으면 에러를 일으키는 경우가 많다. 특히 같은 라이브러리인데 타입스크립트를 사용할 때에 사용방법이 드라마틱하게 달라지는 경우가 종종 있다.
  • 컴파일시 타입을 좀더 엄격하게 체크하는 여부를 끄거나 켤 수 있다.
  • 타입스크립트로 인한 모든 에러는 빌드타임(컴파일)에만 확인할 수 있다. 즉 런타임에서 일어나는 에러는 잡을 수 없다. 서버와 클라이언트의 데이터 형태가 일치하지 않을 때, 타입스크립트는 오히려 프로그램이 안전하다는 '착각'을 일으킬 수 있다. 따라서 타입스크립트를 사용하더라도 테스트 작성을 꼼꼼히 해야 한다.
  • 타입스크립트는 기능 유지보수가 아닌 새로운 기능을 추가할 때 타입을 같이 정의해야 하므로 일반적인 기능 구현에 더 많은 시간이 소모된다.
  • 타입스크립트에 강제 형변환을 유도하고자할 때에는, 강제 형변환이 결국에 이미 지정된 타입을 무너뜨릴 수 있다는 것을 잘 알고 있어야 한다. 지정되지 않은 데이터에 대해서는 강제 형변환을 이용해야하지만, 그렇지 않은 경우는 최대한 지정된 타입을 활용해야 한다.
  • 타입스크립트는 프로그래밍 업계의 수많은 관례를 따르고 있다. 특히 자바와 닮은 부분이 많다. 정적 타입 환경에서 개발하다가 동적 타입 환경에서 개발하는 개발자를 배려하는 환경이기 때문에, 가능하면 이 관례에 맞추는 것이 팀웍에 도움이 많이 된다.(각종 약어나 값 이름 지정하는 방법) 어찌보면 좀 무리가 되는 이야기일수도 있지만, 다른 타입언어를 공부하거나 자바를 함께 공부하는 것이 좋다.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment