Language/Javascript

Typescript - 타입스크립트 입문 정리

둉이 2022. 2. 5. 19:40

 

Typescript

  • 타입 + 자바스크립트
  • 브라우저에서 실행하려면 별도의 컴파일 과정 필요
  • 장점
    • 에러의 사전 방지
      • 의도하지 않은 타입의 인자가 전달되었을 때 런타임(브라우저)이 아닌 개발 단계에서 오류를 탐지하여 수정할 수 있음
    • 코드 가이드 및 자동 완성 용이
      • VSCode에서 해당 변수의 타입에 맞는 메소드 자동 완성
  • 타입이라는 개념은 크게 아래 5가지를 의미
    • type alias
    • interface
    • class
    • enum
    • import 구문

 

JSDoc

  • 소스코드 파일에 주석을 달기 위해 사용되는 마크업 언어
  • 자바스크립트에서 타입스크립트처럼 코딩 가능
  • typedef / property를 사용하여 타입에 대한 힌트 제공 가능
/**
 * @typedef {object} Address
 * @property {string} city
 * @property {string} street
*/

/**
 * @typedef {object} User
 * @property {string} name
 * @property {string} email
 * @property {Address} address
*/

/**
 * @returns {Promise<User>}
*/
const fetchUser = () => {
	return fetch(USER_URL).then(response => response.data);
}
// @ts-check
/**
 * @param {number} a 첫 번째 숫자
 * @param {number} b 두 번째 숫자
*/
const sum = (a, b) => {
	return a + b;
}
  • ts-check
    • tsconfig의 checkJs의 역할

 

타입스크립트 컴파일

# npm i -g typescript
# tsc index.ts  // index.ts를 index.js로 컴파일

 

기본 타입

  • 총 12가지가 있음
  • Number, String, Boolean, Null, Undefined
  • Object, Array
    let arr: number[] = [1,2,3];
    // 혹은
    let arr: Array<number> = [1,2,3]; 

 

  • Tuple
    • 고정 길이 타입 배열
    let arr: [string, number] = ['hi', 10];
    

 

  • Enum
    • 특정 값들의 집합
  • Void
    • null 또는 undefined 값만 가질 수 있음
  • Never
    • while(true)처럼 끝나지 않음
  • Any
    • 모든 가능한 타입을 의미

 

함수 타입

  • 파라미터 + 리턴 값에 각각 타입 지정
    • 리턴 값은 자동 타입 추론하기도 함
  • 선언한 함수의 파라미터와 인자(타입, 개수)가 다를 경우 Error

 

타입 지정

  • :을 이용하여 해당 변수(or 함수)에 타입 지정
const num: number = 123;
const str: string = 'this is string';

 

  • optional 파라미터(?:)
    • 속성을 선택적으로 사용할 수 있음
    interface User {
    	name: string;
    	age?: number;
    }
    

 

  • 읽기 전용 속성(readonly)
    • 변경 불가능한 속성 앞에 readonly를 붙여 선언
    interface User {
    	readonly name: string;
    	age?: number;
    }
    

 

  • 읽기 전용 배열(ReadonlyArray)
    • 읽기 전용 배열을 생성할 수 있음
    const arr: ReadonlyArray<number> = [1, 2, 3, 4];
    

 

  • 유니온 타입(|)
    • 여러 개의 타입 지정 가능
    const logMessage = (value: string | number) => {
      console.log(value);
    };
    
    • 특정 타입으로 추론되므로 메소드 자동완성에 용이
    • 함수 내에서 별도의 타입 가드를 해주지 않는 이상, 여러 개의 타입의 공통 속성에만 접근 가능

 

  • 인터섹션 타입(&)
    • 여러 타입을 하나로 합치는 연산자
    type Value = string & number;  // 가능하지 않은 타입 === Never
    type NewPerson = Developer & Person;
    

 

  • 함수의 타입 지정
    • 함수의 파라미터와 반환 값을 지정
    interface SumFunc {
      (a: number, b: number): number;
    }
    
    const SumFunc: SumFunc = (a, b) => {
      return a + b;
    };
    

 

  • 클래스의 타입 지정
    • 인터페이스를 implements하여 타입 지정
    interface CraftBeer {
      beerName: string;
      nameBeer(beer: string): void;
    }
    
    class myBeer implements CraftBeer {
      beerName: string = 'Baby Guinness';
      nameBeer(b: string) {
        this.beerName = b;
      }
      constructor() {}
    }
    

 

인터페이스(interface)

  • 객체나 함수의 스펙, 클래스, 배열에 타입을 지정하는 데 사용
    • 타입 뿐만 아니라 객체의 프로퍼티도 정의 가능
  • 인터페이스 내에 지정되지 않은 속성 사용 시 오류 발생
    • 타입 추론 오류를 피하기 위해서 as 키워드를 사용하여 타입 단언
  • extends 키워드를 사용하여 확장 가능
  • 동적 속성을 지정할 수 있음
    • [ ] 내에 props를 이름으로 한 동적 속성을 추가할 수 있음
    interface User {
      name: string;
      age: number;
      [propName: string]: any;
    }
    
    const newUser: User = {
      name: 'newUser',
      age: 25,
      newCol: 'newCol',
      newCol2: 'newCol2',
    };
    
  • 타입 별칭(type alias)
    • 특정 타입이나 인터페이스를 참조할 수 있는 타입 변수
  • interface vs type
    • 인터페이스는 동일한 이름으로 재선언 가능
    • 확장 방식이 다름
      • 인터페이스는 extends, 타입은 상속 불가
    • 타입 방식은 VSCode 상에서 프리뷰 기능을 통해 해당 타입 내의 속성 정보도 조회 가능

 

이넘(Enum)

  • 특정 값들의 집합을 의미하는 자료형
enum Direction {
	Up,
	Doun,
	Left,
	Right,
}
  • 초기값을 주지 않으면 숫자형 이넘으로 동작하며, 첫 번째 요소부터 0, 1, 2, ... 1씩 증가
  • 초기값을 주면 해당 값부터 1씩 증가
  • 런타임에 객체 형태로 존재
    • keyof를 사용해야 하는 상황에서는 keyof typeof로 사용
  • 종류
    • 숫자형 이넘
    • 문자형 이넘
      • 이넘의 모든 요소를 전부 초기화해줘야 함
    • 복합 이넘
      • 숫자형 + 문자형
  • 드롭다운이나 특정 예외 상황을 처리할 때 사용하면 좋음
  • 리버스 매핑
    • key를 이용하여 value 접근 + value를 이용하여 key 접근이 가능
    • 숫자형 이넘만 가능
    enum Day {
    	Sunday,
    	Monday,
    	Tuesday,
    }
    const val = Day.Monday;  // 1
    const key = Day[val]  // Monday
    

 

클래스

  • 기존 생성자 함수 기반의 문법적 편리성을 향상한 개념
class Person {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}
  • 클래스 내부에서 사용하는 프로퍼티는 반드시 클래스 상위에 타입을 선언해야 함
  • 기본적으로 프로퍼티는 public으로 동작
    • 클래스 내부에서만 접근 가능한 프로퍼티는 private 키워드를 추가
    • 읽기 전용 프로퍼티는 readonly 키워드를 추가
    • 추상 프로퍼티(메소드)는 abstract 키워드를 추가
    • get, set 키워드를 사용하여 getter/setter 지정 가능
  • 프로토타입
    • 객체를 생성할 때 중복되는 프로퍼티에 대한 상속 구현 가능
      • 부모 프로토타입의 프로퍼티를 참고하여 사용
    • 활용 사례
      • 빌트인 자바스크립트 API
        • Array.prototype의 push, pop 등
        • Object.prototype의 hasOwnProperty 등
  • 추상 클래스
    • class 키워드 앞에 abstract 키워드를 붙여 선언 가능
    abstract class StudentImpl {
    	abstract studentNo : number;  // 반드시 구현해야 함
    	patrt: string;  // 구현 안해도 됨
    	abstract study(subject: string): void;
    	sleep(): void;
    }
    
    class Student {
    	studentNo: number;  // 기본값이 있는 경우에는 =로 지정
    
      constructor(studentNo: number, part: string) {
        this.studentNo = studentNo;
      }
    
      study(subject: string): void {
        console.log(`I'm studying ${subject}`);
      }
    }
    

 

제네릭

  • 타입을 함수의 파라미터처럼 사용하는 것
  • 중복되는 로직을 줄일 수 있음
const logTest = <T>(text: T): T => {
  console.log(text);
  return text;
};

logTest<number>(10);
logTest(10);
logTest<string>('hello world');
  • 자동 타입 추론이 가능한 경우에는 <> 내에 타입을 전달하지 않아도 됨
  • 타입을 전달받아 사용하므로 특정 타입에서만 제공되는 메소드를 사용하지 못하는 경우 발생
    • 해결 방법
      • 배열의 length 메소드를 사용하고자 할 경우에는 Array 자체를 제너릭으로 전달받는 것이 아닌 배열의 요소 타입을 전달받도록 코드 수정
      const logText<T> = (text: T): T => {
      	console.log(text.length);
      	return text;
      }
      

 

  • 제네릭 vs 유니온 타입(|)
    • 유니온 타입은 해당 타입에서 공통적으로 갖고 있는 메소드만 함수 내에서 사용 가능
    • 제네릭은 인자로 전달한 타입
  • 인터페이스에 제네릭 적용
    interface Item<T> {
      value: T;
      selected: boolean;
    }
    
    const emails: Array<Item<string>> = [
      { value: 'naver.com', selected: true },
      { value: 'gmail.com', selected: false },
      { value: 'hanmail.net', selected: false },
    ];
     

 

  • 제네릭의 타입 제한
    • length 메소드의 경우에는 제네릭에서 사용하려고 할 때 오류 발생
    • 힌트 용도의 타입을 생성하여 extends 키워드로 타입 추론 힌트 전달
    interface LengthType {
      length: number;
    }
    
    const logTextLength = <T extends LengthType>(text: T): number => {
      return text.length;
    };
    
    • keyof를 이용하여 이미 선언된 타입의 요소만 허용 가능
    interface ShoppingItem {
      name: string;
      price: number;
      stock: number;
    }
    
    // ShoppingItem의 키인 'name', 'price', 'stock'만 T에 전달 가능
    const getShoppingItemOption = <T extends keyof ShoppingItem>(ItemOption: T): T => {
      return ItemOption;
    };
    

 

타입 추론

  • 타입스크립트가 자동적으로 변수 선언 혹은 초기화 시 타입을 추론하는 것
  • 기본적으로는 any, 파라미터의 기본 값이 있는 경우에는 해당 값의 타입으로 추론
  • Duck Typing
    • 객체의 변수 및 메소드의 집합이 객체의 타입을 결정
  • Structural Subtyping
    • 객체의 실제 구조나 정의에 따라 타입을 결정

 

타입 호환

  • 변환할 타입에서 요구하는 속성을 모두 갖고 있는 경우에는 타입 호환 가능
    • interface, type, class 키워드와는 상관 없음
    interface Developer2 {
      name: string;
      skill: string;
    }
    
    interface Person2 {
      name: string;
    }
    
    let dev: Developer2;
    let p: Person2;
    
    dev = p;  // 타입 호환 불가
    p = dev;  // 타입 호환 가능
    

 

  • 이넘의 타입 호환
    • 숫자형 이넘은 number 타입과 호환되지만 이넘끼리는 호환 X
  • 클래스의 타입 호환
    • static 프로퍼티와 constructor를 제외하고 비교
  • 함수의 타입 호환
    • 파라미터의 종류에 따라 타입 호환
    let add = (a: number) => {
      return a;
    };
    
    let sum = (a: number, b: number) => {
      return a + b;
    };
    
    add = sum;  // 타입 호환 불가
    sum = add;  // 타입 호환 가능
    

 

  • 제네릭의 타입 호환
    • 동적 타입 T가 할당된 후를 기준으로 비교
    • 요소의 타입이나 종류가 다르면 호환 불가

 

타입 단언

  • 개발자가 타입을 더 잘 알고 있는 경우, as 키워드를 사용하여 타입을 지정해주는 것
  • 사용 예시
    • DOM API를 조작할 때 사용
      • querySelector 등의 DOM API를 사용하면 HTML 요소가 아닌 null이 반환될 수 있으므로 타입 단언을 이용하여 해당 요소의 메소드에 정상적으로 접근하도록 함
    • 유니온 타입(|) 사용
      • 유니온 타입의 공통적인 프로퍼티에만 접근 가능
      • 타입 목록 중 하나의 타입으로 단언하여 해당 타입의 프로퍼티에 접근

 

타입 가드

  • 타입 단언으로 일일이 예외처리하는 것이 아닌 타입을 추론하기 위한 별도의 함수를 정의하여 사용하는 것
interface Developer {
  name: string;
  skill: string;
}

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

const introduce = (): Developer | Person => {
  return {
    name: 'Tony',
    age: 33,
    skill: 'Iron Making',
  };
};

const isDeveloper = (target: Developer | Person): target is Developer => {
  return (target as Developer).skill !== undefined;
};

const tony = introduce();
const isTonyDev = isDeveloper(tony);
console.log(isTonyDev ? tony.skill : tony.age);

 

타입 모듈화

  • ES6 Modules 개념과 유사
    • 모듈 스코프, export / import 키워드 등

 

d.ts 파일

  • 타입스크립트 코드의 타입 추론을 돕는 파일

 

tsconfig.json 파일

  • 타입스크립트를 자바스크립트로 변환할 때의 설정을 정의하는 파일
  • tsc 명령어
    • 타입스크립트 → 자바스크립트 컴파일 명령어
  • 속성
    • files
      • 컴파일할 개별 파일 지정
    • include
      • 컴파일할 폴더 경로 지정
    • exclude
      • 컴파일하지 않을 폴더 경로 지정
      • 기본적으로 node_modules, bower_components 폴더 제외
    • extends
      • 다른 타입스크립트 설정 파일을 import하여 추가
    • target
      • 컴파일했을 때 생성되는 자바스크립트 결과물의 버전
      • 기본값은 es3 / 그 외 es5, es6, esnext 등
    • lib
      • 컴파일할 때 포함될 라이브러리의 목록
      • { "lib": ["es2015", "dom", "dom.iterable"] }
    • allowJs
      • 자바스크립트 파일도 함게 포함하여 컴파일할지에 대한 여부

 

전역 변수 선언

  • 맨 앞에 declare 키워드를 붙여 전역 변수/함수 선언

 

인덱싱

  • 숫자로 인덱스에 접근하도록 인터페이스 선언
interface StringArray {
  [index: number]: string;
}

const arr: StringArray = ['Thor', 'Hulk'];
arr[0]; // 'Thor'
  • 배열 요소의 변경을 제한하기 위해서는 인덱스 요소 앞에 readonly 키워드 추가

 

유틸리티 타입(제네릭 타입)

Partial

  • 특정 타입의 부분 집합을 만족하는 타입 정의
interface Address {
  email: string;
  address: string;
}

type MayHaveEmail = Partial<Address>;
const me: MayHaveEmail = {}; // 가능
const you: MayHaveEmail = { email: 'test@abc.com' }; // 가능
const all: MayHaveEmail = { email: 'capt@hero.

com', address: 'Pangyo' }; // 가능

 

Pick

  • 특정 타입에서 몇 개의 속성을 선택하여 타입 정의
interface Product {
  id: number;
  name: string;
  price: number;
  brand: string;
  stock: number;
  something: object;
}

const displayProductDetail = (id: number): Pick<Product, 'id' | 'name' | 'price'> => {
  // return ...;
};

 

Omit

  • 특정 타입에서 지정된 속성만 제거한 타입 정의
interface AddressBook {
  name: string;
  phone: number;
  address: string;
  company: string;
}

const phoneBook: Omit<AddressBook, 'address'> = {
  name: '재택근무',
  phone: 12342223333,
  company: '내 방'
}

const chingtao: Omit<AddressBook, 'address'|'company'> = {
  name: '중국집',
  phone: 44455557777
}

 

맵드 타입(Mapped Type)

기존에 정의된 타입을 새로운 타입으로 변환하는 문법

type Subset<T> = {
  [K in keyof T]?: T[K];
}

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

const ageOnly: Subset<Person> = { age: 23 };
const nameOnly: Subset<Person> = { name: 'Tony' };
const ironman: Subset<Person> = { age: 23, name: 'Tony' };
const empty: Subset<Person> = {};
  • 자바스크립트의 map 고차함수와 유사한 역할