Published on

Typescript 정리

글쓴이

    📌 목차

    Typescript
    • Intro
    • Type Basic
    • Interface
    • Type Alias
    • Generic
    • Function

    Intro

    타입스트립트 사용법

    • 자바스크립트 파일의 확장자가 .js인것처럼, 타입스트립트 파일의 확장자는 .ts이다.
    • tsc 명령어를 통해 타입스크립트 컴파일러를 별도로 사용해 .js 파일로 변환한다.
      • npm install -g typescript : 글로벌로 타입스크립트 설치
    • 코드검사, 수정 등을 도와주는 에디터를 사용하는 것이 편리하다. ex) vscode, webstorm

    타입스크립트의 기능

    • 자바스크립트의 수퍼셋으로의 기능을 가지고 있다.
    • 객체지향과 정적타입 문법을 지원한다.

    타입스크립트 프로젝트세팅

    • 타입스크립트 컴파일 옵션을 tsconfig.json 파일을 통해 관리할 수 있다.
    • 해당 파일을 프로젝트 루트 경로에 생성하여 프로젝트별로 컴파일 옵션을 관리할 수 있다.
    • https://www.typescriptlang.org/play 에서 타입스크립트 코드가 자바스크립트언어로 어떻게 변환되는지 확인할 수 있다.
    • https://replit.com/new/typescript 에서 간단한 타입스크립트 프로젝트를 생성할 수 있다.

    로컬에서 프로젝트세팅하기

    • 로컬에서는 프로젝트 폴더 하위에 npm install -D typescript parcel-bundler를 추가하고, tsconfig.json를 만들어 사용하면 편리하다.
    • tsconfig.json예시
    {
      "compilerOptions": {
        "strict": true,
        "target": "ES6",
        "lib": ["ES2015", "DOM"],
        "module": "CommonJS"
      },
      "include": [
        "src/**/*.ts"
      ],
      "exclude": [
        "node_modules"
      ]
    }
    
    • 로컬 프로젝트 예시 구조
    ├── index.html
    ├── package-lock.json
    ├── package.json
    ├── src
    │   └── main.ts
    └── tsconfig.json
    
    • main.ts에 타입 스크립트 코드를 작성하고, index.html 에서 타입스크립트를 참조시킨다.
    • npx parcel index.html을 통해 빌드하면 로컬에서 돌아간다.

    Type Basic

    타입지정 기본

    변수 오른쪽에 : type_name과 같은 형태로 타입을 명시한다.

    const num : number = 1;
    const num : number = "1" // ERROR!! Type 'string' is not assignable to type 'number'.
    const str : string = "string"
    

    배열은 다음과 같이 타입을 선언할 수 있다.

    case 1: 하나의 원시타입을 원소로 가지는 배열

    const strArr : string[] = ['a','b','c'];
    const strArr2 : Array<string> = ['a','b','c'];
    

    case 2: 두개의 원시타입을 원소로 가질 수 있는 배열

    • | 연산자로 타입을 연결시킨 것을 union type이라고 하며, or 조건과 같이 두개의 타입을 모두 허용한다.
    const arr : (string : number)[] = ['a','b','c',1,2,3];
    

    case 3: 모든 타입을 허용하는 배열

    • 배열의 타입을 단언할 수 없을 때 사용한다.
    const anyArr : any[] = ['a',true,1];
    

    case 4 : interface나 user-define type을 원소로 가지는 배열

    interface User {
        id : string, 
        name : string,
        isAdmin : boolean
    }
    const userArr : User[] = [
        {
            id: 'fjdkfj',
            name : "Tina",
            isAdmin : true
        }
    ]
    

    튜플 타입지정

    • 튜플은 고정된 길이의 배열을 표현한다.
    • 고정된 길이의 배열이기 때문에 특정인덱스에 위치하는 타입을 명시할 수 있다.
    • 재할당할수 없다.(assign x)
    • push, spllice는 가능하다.
    let users [string, string ,boolean][];
    user = [['1','Tina',true],['2','Lion',false]];
    

    enum 타입지정

    • 매핑값은 0부터 시작된다.
    • 문자열 매핑해서 사용할 수 있지만, 개별로 매핑해야 한다.
    enum Week {
        Mon,
        Tue,
        Wed,
        Thu,
        Fri,
        Sat,
        Sun
    }
    enum Size {
        S = 'small',
        M = 'medium',
        L = 'large'
    }
    

    any, unknown 타입지정

    • any로 타입을 지정하면 바닐라 js에서 변수를 할당하는 것과 동일하게 사용할 수 있다.
    • unknown으로 타입을 지정하면 나머지는 any와 동일하지만 해당 변수를 다른 타입인 변수에 대입할수 없다. as type_name으로 타입을 특정하면 가능.
    • unknown보단 보다 명확한 타입을 사용하는 것이 좋다.
    const anyNum : any = 1
    let num = anyNum;
    const unknownNum : unknown = 1
    num = unknownNum // error
    num = unknownNum as number
    

    object 타입 지정

    • object는 최상위 객체를 포함한 모든 객체를 의미하기 때문에 요소의 타입을 지정하거나 interface를 사용한다.
    let obj : object = null;
    // type
    let user1 : {id:string, name:string, isAdmin:boolean} = {
            id: 'fjdkfj',
            name : "Tina",
            isAdmin : true
    }
    //interface
    interface User {
        id : string, 
        name : string,
        isAdmin : boolean
    }
    

    함수

    • void 로 반환값이 없는 함수로 명시할 수 있다.
    • never는 항상오류는 출력하거나 어떤값도 반환하지 않을때 사용한다.
    • 화살표 함수를 통해 타입을 지정할 수 있다.
    let myFunc : () => void; 
    

    union, intersection

    • | 연산자로 타입을 연결시킨 것을 union type이라고 하며, or 조건과 같이 두개의 타입을 모두 허용한다.
    • & 연산자로 타입을 두개다 포함시킬 수 있다. 새로운 타입을 생성하지 않고 기존의 타입을 조합해 사용하는 방법이다.

    타입추론

    • typescript에서 모든 상황에 타입을 명시해야 하는 것은 아니다.
    • 타입이 명확해, 타입을 추론해주는 경우에는 프로그래머가 타입을 명시할 필요가 없다.
      • 초기화된 변수
      • 기본값이 설정된 함수의 매개변수
      • 반환값이 있는 함수

    타입단언

    • 말그대로 프로그래머가 타입에 대해 확신을 가지고 단언하는 것이다.
    • 함수의 로직은 숫자만 받아들이지만, 문자열도 숫자로 변환해 사용하고 싶은 경우에 사용한다.
    • 프로그래머가 타입스트립트에게 타입에 대한 힌트를 주는 것이다.
    • 타입단언을 통해 컴파일 타임에서 타입에 대한 처리를 마무리할 수 있다.
    • <Type>이나 as Type을 통해 타입을 단언할 수 있으며, 전자는 jsx문법과 충돌이 발생하기도 한다.

    타입가드

    • 타입단언을 여러번 사용해야 하는 경우등 일정한 범위의 타입을 단언할때 타입가드를 통해 처리한다
    • is,typeof, in, instanceof 등의 연산자를 사용해 타입가드를 처리할 수 있다.

    Interface

    • interface 키워드를 통해 클래스의 스펙을 정의하거나, 객체로서 활용할 수 있다.
    • 멤버변수를 구분할때, ,,; 로 구분하거나 개행으로만 구분할 수 있다.
    • 변수명 뒤에 ?를 붙여 옵셔널한 변수를 선언할수도 있다.
    • 변수명 앞에 readonly를 붙여 할당뒤에 값 수정을 방지할 수 있다.
    • 모든 멤버가 readonly라면 유틸리티나 타입단언을 사용해 문법적으로 간결하게 작성할 수 있다.
    interface IUser {
      name: string,
      age: number
    }
    // Or
    interface IUser {
      name: string;
      age: number;
    }
    // Or
    interface IUser {
      name: string
      age: number
    }
    interface IUser {
      name: string
      readonly age: number
      isAdmin? : boolean
    }
    
    // Readonly Utility
    interface IUser {
      name: string,
      age: number
    }
    let user: Readonly<IUser> = {
      name: 'Neo',
      age: 36
    };
    user.age = 85; // Error
    user.name = 'Evan'; // Error
    
    // Type assertion
    let user = {
      name: 'Neo',
      age: 36
    } as const;
    user.age = 85; // Error
    user.name = 'Evan'; // Error
    

    함수 in interface

    • 함수의 시그니처를 인터페이스에 선언할 수 있다.
    interface IName {
      (PARAMETER: PARAM_TYPE): RETURN_TYPE // Call signature
    }
    
    interface IUser {
      name: string
    }
    interface IGetUser {
      (name: string): IUser
    }
    
    const getUser: IGetUser = function (n) { // n is name: string
      // Find user logic..
      // ...
      return user;
    };
    getUser('Heropy');
    

    클래스 of interface

    • interface의 정해진 시그니처 형태로 구현하기 위해 interface를 상속받을수 있다.
    interface IUser {
      name: string,
      getName(): string
    }
    
    class User implements IUser {
      constructor(public name: string) {}
      getName() {
        return this.name;
      }
    }
    
    const neo = new User('Neo');
    neo.getName(); // Neo
    
    • interface는 extends 키워드를 통해 interface 간의 상속이 가능하다.
    interface IAnimal {
      name: string
    }
    interface ICat extends IAnimal {
      meow(): string
    }
    
    class Cat implements ICat { // Error - TS2420: Class 'Cat' incorrectly implements interface 'ICat'. Property 'name' is missing in type 'Cat' but required in type 'ICat'.
      meow() {
        return 'MEOW~'
      }
    }
    

    구성 시그니처

    • interface 형태의 객체를 호출하려면 new 키워드를 사용해 구성 시그니처를 선언해야 한다.
    
    // 구성 시그니처
    interface ICat {
      name: string
    }
    interface ICatConstructor {
      new (name: string): ICat;
    }
    
    class Cat implements ICat {
      constructor(public name: string) {}
    }
    
    function makeKitten(c: ICatConstructor, n: string) {
      return new c(n); // ok
    }
    const kitten = makeKitten(Cat, 'Lucy');
    console.log(kitten);
    

    indexable type

    • 숫자나 문자열 형태로 접근이 가능한 타입을 indexable type이라고 한다.
    • interface에서도 해당 feature를 이용할 수 있다.
    interface IUser {
      [userProp: string]: string | boolean
    }
    let user: IUser = {
      name: 'Neo',
      email: 'tinajeong@gmail.com',
      isValid: true,
      0: false
    };
    console.log(user['name']); // 'Neo' is string.
    console.log(user['email']); // 'tinajeong@gmail.com' is string.
    console.log(user['isValid']); // true is boolean.
    console.log(user[0]); // false is boolean
    console.log(user[1]); // undefined
    console.log(user['0']); // false is boolean
    

    Type Alias

    • type 키워드를 사용해 타입의 별칭을 만들수 있다. 일종의 user-defined 타입이다.
    type MyType = string;
    type YourType = string | number | boolean;
    type TUser = {
      name: string,
      age: number,
      isValid: boolean
    } | [string, number, boolean];
    
    let userA: TUser = {
      name: 'Neo',
      age: 85,
      isValid: true
    };
    let userB: TUser = ['Evan', 36, false];
    
    function someFunc(arg: MyType): YourType {
      switch (arg) {
        case 's':
          return arg.toString(); // string
        case 'n':
          return parseInt(arg); // number
        default:
          return true; // boolean
      }
    }
    

    Generic

    • 타입을 선언 시점이 아닌, 사용하는 시점에 타입을 명시할 수 있는 문법이다.
    function toArray<T>(a: T, b: T): T[] {
      return [a, b];
    }
    
    toArray<number>(1, 2);
    toArray(1, 2); // 타입추론
    toArray<string>('1', '2');
    toArray<string | number>(1, '2');
    toArray<number>(1, '2'); // Error
    
    • 제약조건을 넣어 일부타입으로 제너릭 사용을 제한 시킬수도 있다.
    • 3개 이상의 타입을 명시하려면 type 키워드를 사용한다.
    • 제약조건은 extends와 함께만 사용될 수 있다.
    interface MyType<T extends string | number> {
      name: string,
      value: T
    }
    type U = string | number | boolean 
    
    interface IUser<T extends U> {
      name: string,
      age: T
    }
    

    Function

    this, bind

    • callback 함수등 호출하지 않은 함수가 있을 경우에는 bind를 이용해 매핑해준다.
    obj.b(); // Hello~
    
    const b = obj.b.bind(obj);
    b(); // Hello~
    
    function someFn(cb: any) {
      cb();
    }
    someFn(obj.b.bind(obj)); // Hello~
    
    setTimeout(obj.b.bind(obj), 100); // Hello~
    

    참고