TypeScript 기본


🌟 JS의 문제점

  • 자료형을 넘나드는 계산을 수행한다.
  • 따라서 다음과 같은 코드에서 에러를 보여주지 않는다.
  • 타입스크립트에서는 다음과 같이 에러를 보여준다.
  • TS 컴파일 방법

  • npx tsc (파일이름).ts
  • tsc -w
  • 그러나 React에서 사용 시 애초에 typescript로 세팅하면 이러한 것들을 해주지 않아도 된다.
  • 🥕 기본 타입

    기본 타입 지정 방법

    const car: string = 'bmw';
    const age: number = 30;
    const a: number[] = [1, 2, 3];
    const a2: Array<number> = [1, 2, 3];
    const week1: string[] = ['mon', 'tue', 'wed'];
    const week2: Array<string> = ['mon', 'tue', 'wed']; // Generic
    week1.push(1); // ERROR
  • 기본적인 JS문법에서 변수명 뒤에 :를 이용하여 타입을 붙여준다.
  • 해당 변수에는 붙여준 타입의 데이터만 할당할 수 있다.
  • 따라서 위 코드에서 week1 배열에 숫자 타입 데이터를 push하면 에러가 난다.
  • 배열의 타입은 데이터타입[] 또는 Array<데이터타입>으로 결정할 수 있다.
  • 튜플 (Tuple)

    let b: [string, number];
    b = ['abc', 1];
    b = [1, 'abc']; // ERROR
    b[0].toLowerCase();
    b[1].toLowerCase(); // ERROR
  • 튜플은 각 요소의 타입이 고정된 배열이다.
  • 튜플은 배열의 길이도 고정되어 있다.
  • 위 코드에서 b라는 배열의 0번 인덱스에는 string 타입만, 1번 인덱스에는 number 타입만 할당 될 수 있다.
  • 따라서 0번 인덱스에 number 타입을 할당하면 에러가 난다.
  • .toLowerCase()는 string 타입에만 사용할 수 있는 메소드 이므로 b[1](number 타입)에 해당 메소드를 사용하면 에러가 난다.
  • Null, Undefined

    const n: null = null;
    const u: undefined = undefined;
  • null 또는 undefined 타입도 선언해줄 수 있다.
  • Enum

  • Enum(열거형)은 연관된 아이템들을 묶어서 표현할 수 있는 수단이다.
  • enum Os { // 보통 enum의 이름은 대문자로 시작한다.
    Window,
    Ios,
    Android,
    }
    let myComputer: Os;
    myComputer = Os.Window;
    myComputer = 'Window'; // ERROR
    console.log(myComputer); // 0
  • 예시로 Os 라는 enum을 생성하면, 이를 타입처럼 사용할 수 있다.
  • enum의 이름을 myComputer라는 변수에 타입으로 선언하면 해당 enum의 내용만 할당할 수 있다.
  • 따라서 문자열로 ‘Window’라고 적어서 할당한다고 해도 에러가 난다.
  • 그런데 여기서 myComputer를 콘솔에 찍었을 때 0이 나오는 이유는 해당 enum이 숫자 열거형이기 때문이다.
  • // 숫자 열거형
    enum Os {
    Window = 1,
    Ios,
    Android = 10,
    }
    console.log(Os.Window); // 1
    console.log(Os.Ios); // 2
    console.log(Os.Android); // 10
  • enum의 각 키워드들이 값으로 갖는 숫자는 다음처럼 지정해줄 수 있다.
  • 지정되지 않으면 이전 값의 다음 값을 가진다.
  • // 문자열 열거형
    enum Os {
    Window = 'Window',
    Ios = 'Ios',
    Android = 'Android',
    }
    console.log(Os.Window); // Window
    console.log(Os.Ios); // Ios
    console.log(Os.Android); // Android
  • 문자열 열거형으로 위처럼 선언하면 각각의 키워드가 문자열을 값으로 갖도록 할 수 있다.
  • Any

  • 모든 타입에 대하여 허용한다.
  • 기존 자바스크립트로 작동하던 서비스를 점진적으로 TS로 변환할 때 활용된다.
  • let str: any = 'hi';
    let num: any = 12;
    let arr: any = [1, 2];

    Object

  • 객체는 다음처럼 타입을 정해 줄 수 있다.
  • let jkeObj: { name: string; age: number; hobbies: string[] } = {
    name: 'jke',
    age: 27,
    hobbies: ['Music', 'BasketBall'],
    };
  • 타입을 지정하는 부분에서는 , 대신 ;를 사용한다.
  • Union

  • 여러 타입 또는 값이 들어올 수 있도록 할 수 있다.
  • // Union
    let unionType: number | string | number[] = 1;
    unionType = 'jke';
    unionType = 1;
    unionType = [1, 2, 3];
    // 이렇게 사용 시 타입스크립트의 의미가 사라지는 것이므로
    // 추천되는 방법은 아니다.
    let gender: 'M' | 'F' = 'M';
    gender = 'F';
    gender = 'T'; // error

    Void

    function sayHello(): void {
    console.log('hello');
    }
  • 함수가 아무것도 반환하지 않는 경우, void 타입으로 선언해줄 수 있다.
  • 위 코드처럼 선언해주면 해당 함수가 무언가를 return 하려고 할 경우 에러가 난다.
  • 해당 위치에 다른 타입을 선언하면 해당 타입만 리턴할 수 있다.
  • Never

    function showError(): never {
    throw new Error();
    }
    function infLoop(): never {
    while (true) {
    // Do Something ...
    }
    }
  • 함수의 return 타입으로 never 타입을 선언해줄 경우 항상 오류를 출력하거나 리턴 값을 절대로 내보내지 않음을 의미한다.
  • 이를 변수에 지정하면 never 가 아닌 타입은 할당할 수 없다.
  • Unknown

    let variable: unknown
    variable = true // OK (boolean)
    variable = 1 // OK (number)
    variable = 'string' // OK (string)
    variable = {} // OK (object)
    let anyType: any = variable // OK
    let booleanType: boolean = variable
    // Error: Type 'unknown' is not assignable to type 'boolean'.(2322)
    let numberType: number = variable
    // Error: Type 'unknown' is not assignable to type 'number'.(2322)
    let stringType: string = variable
    // Error: Type 'unknown' is not assignable to type 'string'.(2322)
    let objectType: object = variable
    // Error: Type 'unknown' is not assignable to type 'object'.(2322)
  • unknown 타입은 any처럼 모든 타입을 허용한다.
  • 하지만 any는 타입 검사를 느슨하게 하여 애플리케이션에서 예기치 못한 문제가 발생할 가능성이 높다.
  • unknown은 위 예제처럼 타입을 체크해주기 때문에 any보다 사용이 권장된다.
  • 🔦 Advanced Type

    literal type

  • 범용적인 string, number 등의 타입이 아니라 정확한 값을 타입으로 지정할 수 있다.
  • const를 사용해 number나 string 등을 선언하면 literal type으로 추론된다.
  • let age:3 = 3;
    age = 5; // Error
    let name:"jke" = "jke";
    name = "jkejkejke" // Error
    // literal type으로 추론
    const age = 1;
    const name = "jke"

    as const

  • 타입을 literal로 정확히 지정할 때 사용한다.
  • 이렇게 지정하면 상수로 지정한 값을 다른 파일에서 작성할 때도 볼 수 있어 편하다.
  • const numbers = [1,2,3] // number[]로 추론
    const numbers = [1,2,3] as const // [1,2,3]
    const obj = {x:1, y:2} // {x:number, y:number}
    const obj = {x:1, y:2} as const // {x:1, y:2}

    Generic

  • 타입을 인자처럼 활용할 수 있다.
  • 아래 상황에서는 해당 함수에 어떤 타입의 array가 들어올 지 모르는 상황이지만 array는 number의 array로 정의되어 있다.
  • function map(array:number[], callback:(...args:any[]) => any;) {
    const result = [];
    for(const element of array){
    result.push(callback(element));
    };
    return result;
    }
    map([1,2,3,4], x => x + 1); // Good
    map(["hello", "world"], x => x.toUpperCase());
    // Type 'string' is not assignable to type 'number'.
  • 이렇게 되면 문자열로 된 array를 map 함수에 인자로 넣어 사용하고 싶을 경우 또 다른 map 함수를 만들어주어야 한다.
  • 이와 같은 상황을 해결하기 위해 Generic을 사용할 수 있다.
  • function map<T>(array:T[], callback:(...args:any[]) => any;) {
    const result = [];
    for(const element of array){
    result.push(callback(element));
    };
    return result;
    }
    map<number>([1,2,3,4], x => x + 1);
    map<string>(["hello", "world"], x => x.toUpperCase());
  • 이렇게 하면 map함수를 사용할 때에 타입을 받아 그 타입이 array의 타입으로 지정되도록 할 수 있다.
  • T는 임의의 이름이고 다른 이름을 사용해도 된다.
  • keyof

  • 객체 타입에서 key만 추출하는 방법이다.
  • type People = {name: string, age:number, gender:"male" | "female", hobby:string};
    type KeyOfPeople = keyof People; // name | age | gender | hobby
    const key:KeyOfPeople = 'name';
    const key2:KeyOfPeople = 'email'; // Error
  • 주의할 점은 object에 쓰는 것이 아니라 type에 쓰는 것이다.
  • typeof

  • 객체 등 값에서 타입을 추출하는 방법이다.
  • const jke = {
    name:"jke",
    age:27,
    gender:"male",
    hobby:"basketball"
    };
    type People = typeof jke;
    /*
    {
    name: string;
    age: number;
    gender: string;
    hobby: string;
    }
    */
    // keyof와 typeof 결합
    type KeyOfPeople = keyof typeof jke;
    // name | age | gender | hobby

    narrowing

  • if문 등을 사용하여 타입의 범위를 좁히는 방법이다.
  • function toUpper(arg:string | number){
    arg.toUpperCase() // Error
    /*
    Property 'toUpperCase' does not exist on type 'string | number'.
    Property 'toUpperCase' does not exist on type 'number'.
    */
    }
    // narrowing
    function toUpper(arg:string | number){
    if(typeof arg === "string"){
    arg.toUpperCase() // OK!
    }
    }
    function addFive(num?:number){
    return num + 5; // Error
    /*
    (parameter) num: number | undefined
    Object is possibly 'undefined'.
    */
    }
    // narrowing
    function addFive(num?:number){
    if(num){
    return num + 5
    } else {
    throw new Error("Argument num is not a number")
    }
    }

    🪃 Interface

    Type을 변수에 넣어서 사용

  • 타입 자체를 변수에 넣어서 사용할 수 있다.
  • // 예시1
    type numOrStr = string | number;
    let data: numOrStr = 1;
    data = '1';
    let typeArr: numOrStr[] = [1, '2', 3, '4'];
    // 예시 2
    type grade = 'A' | 'B' | 'C' | 'D' | 'E';
    const jkeGrade: grade = 'A';

    기본 사용법

  • TS에서 복잡한 객체를 사용할 때 Interface 사용한다.
  • Interface는 type과 다르게 객체에만 사용할 수 있다.
  • 일반 Type과 구분하기 위하여 첫글자를 대문자로 사용한다.
  • interface User {
    name: string;
    id: string;
    age: number;
    isMember: boolean;
    }
    const jkeInfo: User = {
    name: 'jke',
    id: 'j56237',
    age: 27,
    isMember: true,
    };