9 분 소요

타입스크립트란 ?

  • TypeScript는 JavaScript에 추가적인 구문을 추가하여 editor와의 단단한 통합을 지원합니다. editor에서 초기에 오류를 잡을 수 있습니다.

  • TypeScript 코드는 JavaScript가 실행되는 모든 곳(브라우저, Node.js 또는 Deno 및 앱 등)에서 JavaScript로 변환될 수 있습니다.

  • TypeScript는 JavaScript를 이해하고 타입 추론(type inference)을 사용하여 추가 코드 없이도 훌륭한 도구를 제공합니다.

Interface

  • 인터페이스는 일반적으로 타입 체크를 위해 사용되며 변수, 함수, 클래스에 사용할 수 있습니다.
  • 인터페이스는 여러가지 타입을 갖는 프로퍼티로 이루어진 새로운 타입을 정의하는 것과 유사합니다.
  • 인터페이스에 선언된 프로퍼티 또는 메소드의 구현을 강제하여 일관성을 유지할 수 있도록 하는 것입니다.
  • 인터페이스는 프로퍼티와 메소드를 가질 수 있다는 점에서 클래스와 유사하나 직접 인스턴스를 생성할 수 없고 모든 메소드는 추상 메소드입니다.

예제를 보며 살펴보겠습니다.

type Score = 'A' |'B'|'C'|'F';

interface User {
    name: string;
    age : number;
    gender? : string; // ?표시가 있으면 있어도 되고 없어도 된다.
    readonly birtYear : number;
    [grade: number] : Score;
}

let user: User = {
    name: 'xx',
    age: 30,
    birtYear: 2000,
    1 : 'A',
    2 : 'B',
    3 : 'C'
}
user.age = 50;
console.log(user.age); // 50
interface Add {
    (num1: number, num2: number): number;
}

const add: Add = (x, y) => x + y;
console.log(add(23, 20)); // 43
interface IsAdult {
    (age: number): boolean;
}

const a:IsAdult = (age) => {
    return age > 19;
}
console.log(a(20)); // true

Implements

  • implements을 사용하여 클래스가 특정 인터페이스를 충족하는지 확인할 수 있습니다.
  • 클래스를 올바르게 구현하지 못하면 오류가 발생합니다.

  • object를 interface로 typing하듯, class도 interface로 typing한다고 생각하면 됩니다.
  • implements 는 부모의 메소드나 변수를 그대로 가져다 쓰는게 아닌 반드시 오버라이드 해서 사용 해야 한다.
interface Car {
    color: string;
    wheels: number;
    start(): void;
}

class Bmw implements Car {
    color = "red";
    wheels = 3;
    start(){
        console.log('go.....');
    }
}

class Benz implements Car {
    color;
    wheels = 4;

    constructor(c:string) {
        this.color = c;
    }

    start(){
        console.log('go.....');
    }
}

const b = new Benz('white');
const e = new Bmw();
e.start(); // 'go.....'

extends (클래스 상속) -> abstract가 아닌 일반 class extends를 다룬다.

부모에서 선언된 메소드 혹은 변수를 자식은 모두 상속 받아 그대로 사용 할 수 있다.

interface Car {
    color: string;
    wheels: number;
    start(): void;
}

interface Audi extends Car {
    door: number;
    stop(): void;
}

const audi: Audi = {
    color: "blue",
    wheels: 4,
    door: 3,
    start() {
        console.log("heelo");
    },
    stop() {
        console.log("stop")
    }
}

두개 클래스 상속

interface FIFA {
    team: string;
    ball: number;
    start(): void;
}

interface Soccer {
    name: string;
}

interface FIFASoccer extends FIFA, Soccer {
    bigEar: number;
}

const theSoccer: FIFASoccer = {
    team: 'FC Seoul',
    ball: 1,
    start(){
        console.log('fifa');
    },
    name: 'pkb',
    bigEar: 5,
}

함수 타입

  • 함수에서 아무것도 return 해주지 않을 땐 void를 타입으로 써준다.

    function add(num1: number, num2: number): void {
        //	return num1 + num2;
        console.log(num1 + num2);
    }
    
  • return이 있을 땐 타입에 맞게 써준다.

    function isAdult(age: number):boolean {
        return age > 19;
    }
    
  • 인터페이스처럼 함수의 매개변수도 옵셔널로 지정할 수 있습니다.

    function hello(name?: string) { //여기서 name은 선택적 매개변수
        return `Hello, ${name || "world"}`;
    }
      
    const result = hello();
    
  • 나머지 매개변수의 타입 작성법

    function add(...nums: number[]) {
        return nums.reduce((result, num) => result + num, 0);
    }
    add(1, 2, 3); // 6
    

this와 관련된

interface User {
    name: string;
}

const Sam: User = {name: 'pkb'}

function showName(this:User, age: number, gender:'m'|'f') { 
    console.log(this.name, age, gender);
}

const a = showName.bind(Sam);
a(30, 'm'); // "pkb",  30,  "m"     


  • 여기서 this는 사용하는 방법에 따라서 달라집니다. 지금 위 코드에서는 bind메소드를 통해 this 를 Sam 객체로 강제하고 있습니다.

  • 타입스크립트에서는 this를 쓰고 타입을 정해줘야합니다. 만약 매개변수가 있으면 첫번 째 인수로 적어줍니다.

interface User {
    name: string;
    age: number;
}
function join(name: string, age: number | string): User | string {
    if (typeof age === "number") {
        return {
            name,
            age,
        };
    } else {
        return "나이는 숫자로 입력해주세요.";
    }
}

// const sam: User = join("sam", 30);
// const pkb: string = join("pkb", "30");

위 코드를 보면 아무 문제가 없는 것처럼 보이지만 sam 과 pkb에서 오류가 납니다.

이유는 sam이 User 객체를 반환한다는 확신이 없기 때문입니다. 즉 string을 반환할수도 있다고 판단하기 때문입니다.

위 코드에서는 age매개변수의 타입에 따라 결과 값이 달라지고 있습니다. 이럴때는 오버로드를 사용합니다. 함수 오버로드는 전달받은 매개변수의 갯수나 타입에 따라 다른 동작을 하게 하는 것을 의미합니다. 여기 이 함수 위에 똑같은 모습으로 작성을 해주시면 됩니다.

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

//오버로드 사용
function join(name: string, age: string): string;
function join(name: string, age: number): User;
function join(name: string, age: number | string): User | string {
    if (typeof age === "number") {
        return {
            name,
            age,
        };
    } else {
        return "나이는 숫자로 입력해주세요.";
    }
}

const sam: User = join("sam", 30);
console.log(sam); // {"name": "sam","age": 30} 
const pkb: string = join("pkb", "30");
console.log(pkb); // "나이는 숫자로 입력해주세요."

Literal Types

  • TypeScript에는 문자열과 숫자, boolean 세 가지 리터럴 타입이 있는데 이를 사용하면 문자열이나 숫자에 정확한 값을 지정할 수 있습니다.

    • 문자열 Literal Types
    const userName1 = "Bob"; // 이렇게 정해진 스트링값을 가진 것을 문자열 리터럴 타입이라함
    let userName2: string | number = "Tom";
    userName2 = 3;
      
    type Job = "police" | "developer" | "teacher";
      
    interface User {
        name: string;
        job: Job;
    }
      
    const user: User = {
        name: "Bob",
        job: "developer" // 위에 Job에서 선언된것만 사용할 수 있다.
    }
    
    • 숫자형 Literal Types
    interface HighSchoolStudent {
        name: string;
        grade: 1 | 2 | 3; // 학년은 1, 2, 3만 사용할 수 있다.
    } 
    

Union Types

  • 유니온 타입(Union Type)이란 자바스크립트의 OR 연산자(||)와 같이 'A' 이거나 'B'이다 라는 의미의 타입입니다.

    interface Car {
        name: "car";
        color: string;
        start(): void;
    }
      
    interface Mobile {
        name: "mobile";
        color: string;
        call(): void;
    }
      
    function getGift(gift: Car | Mobile) {
        console.log(gift.color);
        if (gift.name == "car") {  // 이런것을 식별가능한 유니온 타입이다.
            gift.start();
        } else {
            gift.call();
        }
    }
    

Intersection Types

  • 인터섹션 타입(Intersection Type)은 여러 타입을 모두 만족하는 하나의 타입을 의미합니다.

    interface Car {
        name: string;
        start(): void;
    }
      
    interface Toy {
        name: string;
        color: string;
        price: number;
    } 
    const toycar: Toy & Car = {         
        name: "타요",           // Toy와 Car 엔퍼서티를 다 적어주어야함
        start(){},
        color: "red",
        price: 5,
    }
    

Class

  • Typescript 클래스는 클래스 몸체에 클래스 프로퍼티를 사전 선언하여야 합니다.

    class Car {      
        color: string; // js에서는 이 코드 color: string를 안써줘도 상관없지만 ts에서는 써줘야함 
        constructor(color:string) {
            this.color = color;
        }
        start(){
            console.log('start');
        }
    }
      
    const bmw = new Car("red");
    
  • Typescript 클래스에서는 사용할 수 있는 접근제한자(Access modifier)

    • public - 자식 클래스, 클래스 인스턴스 모두 접근 가능

      class Car {      
          // color: string;프로퍼티를 미리 선언하지않고 하는방법으로는 public과 readonly 있다.
          constructor(public color:string) { 
              this.color = color;
          }
          start(){
              console.log('start');
          }
      }
          
      const bmw = new Car("red");
      
      class Car {
          public name: string = "car";
          color: string;
          constructor(color: string) {
              this.color = color;
          }
          start() {
              console.log("start");
          }
      }
          
          
      class Bmw extends Car {
          constructor(color: string) {  //public을 통해 super매소드 사용할수있다.
              super(color);
          }
          showName() {
              console.log(super.name);
          }
      }
          
      const z = new Bmw("black");
      
    • protected - 자식 클래스에서 접근가능 public과 다른점으로는 자식클래스 내부에서는 참조할 수 있으나 클래스 인스턴스로는 참조할 수 없다.

      class Car {
          protected name: string = "car";
          color: string;
          constructor(color: string) {
              this.color = color;
          }
          start() {
              console.log("start");
              console.log(this.name);
          }
      }
          
          
      class Bmw extends Car {
          constructor(color: string) {  //public을 통해 super매소드 사용할수있다.
              super(color);
          }
          showName() {
              console.log(super.name);
          }
      }
          
      const z = new Bmw("black");
      // console.log(z.name); 에러가 발생한다. protected는 클래스 인스턴스로는 참조 X
      
    • private - 해당 클래스 내부에서만 접근가능 private 대신 # 사용가능

      class Car {
          private name: string = "car";
          color: string;
          constructor(color: string) {
              this.color = color;
          }
          start() {
              console.log("start");
              console.log(this.name); // private로 인해 여기서만 사용할수있다.
          }
      }
          
          
      class Bmw extends Car {
          constructor(color: string) { .
              super(color);
          }
          showName() {
              // console.log(super.name); private로 인해 사용불가
          }
      }
          
      const z = new Bmw("black");
      
    • readonly - 읽기전용

      class Car {
          readonly name: string = "car";
          color: string;
          constructor(color: string) {
              this.color = color;
          }
          start() {
              console.log("start");
              console.log(this.name); 
          }
      }
          
          
      class Bmw extends Car {
          constructor(color: string) { .
              super(color);
          }
          showName() {
              console.log(super.name);
          }
      }
          
      const z = new Bmw("black");
      console.log(z.name);
      // z.name = 'zzz4'; readonly에 의해서 읽기 전용이라 변경 불가
      
    • static - static을 써주면 정적 변수를 만들 수 있다. this가 아니라 클래스명을 적어주어야합니다.

      class Car {
          name: string = "car";
          color: string;
          static wheels = 4;
          constructor(color: string) {
              this.color = color;
          }
          start() {
              console.log("start");
              console.log(Car.wheels);  // 클래스명을 적어주어야함
          }
      }
      class Bmw extends Car {
          constructor(color: string) {
              super(color);
          }
          showName() {
              console.log(super.name);
          }
      }
          
      const z = new Bmw("black");
      console.log(Car.wheels); // 클래스명을 적어주어야함
      
    • 추상 class - 클래스 앞에 abstract 적어주어서 사용할 수 있습니다.

      • new를 이용하여 객체를 만들수는 없습니다. 오직 상속을 통해서만 사용가능합니다.
      • 추상 클래스는 오직 다른 클래스가 상속받을 수 있는 클래스입니다. 하지만 직접 새로운 인스턴스를 만들 수는 없습니다.
      abstract class Car {
          color: string;
          constructor(color: string) {
              this.color = color;
          }
          start() {
              console.log("start");
          }
          abstract doSomething(){}  //이렇게 abstract을 써주면 상속 받는쪽에서 구체적인 기능을 구현해줘야함
      }
          
      // const car = new Car("red") 불가능
          
          
      class Bmw extends Car {
          constructor(color: string) {
              super(color);
          }
          doSomething() {
              alert(3); // 이렇게 여기에 구체적인 기능을 구현해줘야 에러가 안남.
          }
      }
          
          
      const z = new Bmw("black");
      

Generic

  • Generic 이용하면 클래스나 함수 인터페이스를 다양한 타입으로 재사용할 수 있습니다.

  • 선언할 때는 타입 파라미터만 적어주고 생성하는 시점에 사용하는 타입을 결정하는 것입니다.

    function getSize<T>(arr: T[]): number {
        return arr.length;
    }
      
    const arr1 = [1, 2, 3];
    getSize<number>(arr1); // 3
      
    const arr2 = ["a", "b", "c"];
    getSize<string>(arr2); // 3
      
    const arr3 = [false, true, true];
    getSize<boolean>(arr3); // 3
    
    • 옵션으로 사용할 때

      interface Mobile<T> {
          name: string;
          price: number;
          option: T;
      }
          
      const m1: Mobile<object> = {
          name: "pkb",
          price: 1000,
          option: {
              color: "red",
              coupon: false,
          }
      }
      const m2: Mobile<string> = {
          name: "pkb",
          price: 1000,
          option: "hello",
      }
      
    • 확장으로 사용 할 때

      interface User {
          name: string;
          age: number;
      }
          
      interface Car {
          name: string;
          color: string;
      }
          
      interface Book {
          price: number;
      }
          
      const user: User = {name: "A", age: 3};
      const car: Car = {name: "bmw", color: "red"};
      const book: Book = { price: 1000 };
          
      function showLook<T extends { name:string }>(data:T): string {
          return data.name;
      }
          
      showLook(user);
      showLook(car);
      // showLook(book); book에는 name이 없어서 에러남
      

Utility Types

  • keyof - key값들을 유니온 형태로 받을 수 있습니다.

    interface User {
        id: number;
        name: string;
        age: number;
        gender: "m" | "f";
    }
      
    type UserKey = keyof User; // 'id' | 'name' | 'age' | 'gender'
    const uk: UserKey = "age";
    const uk1: UserKey = "name";
    
  • Partial 은 프로퍼티를 모두 옵션으로 바꿔줍니다. 그래서 일부만 사용하는게 가능합니다.

  interface User {
      id: number;
      name: string;
      age: number;
      gender: "m" | "f";
  }
  
  let admin:Partial<User> = { // 원래는 age, gender를 포함해야하는데 Partial을 통해 일부만 사용함.
      id: 1,
      name: 'BOD',
  }
  • Required은 프로퍼티를 필수로 바꿔줍니다.

    interface User {
        id: number;
        name: string;
        age?: number;
    }
      
    let people: Required<User> = {  //이렇게 Required를 써주면 age도 필수가 되버린다.
        id: 112,
        name: "pkb",
        age: 22,
    }
    
  • Record<K,T>은 여기서 K는 key이고 T는 type입니다.

    interface Score22 {
        "1" : "A" | "B" | "C" | "D";
        "2" : "A" | "B" | "C" | "D";
        "3" : "A" | "B" | "C" | "D";
        "4" : "A" | "B" | "C" | "D";
    }
      
    const score: Score22 = {
        1: "A",
        2: "B",
        3: "C",
        4: "D",
    }
      
    // 이부분을 Record를 활용해보면
    type Grade = '1'|'2'|'3'|'4';
    type Score ="A" | "B" | "C" | "D" | "F";
      
    const score: Record<Grade, Score> = {
        1: "A",
        2: "B",
        3: "C",
        4: "D",
    }
    
  • Pick<T,K>은 T타입에서 K프로퍼티만 골라서 사용합니다.

    interface User {
        id: number;
        name: string;
        age: number;
        gender: "M" | "W";
    }
      
    const admin: Pick<User, 'id' | 'name'> = {
        id: 0,
        name: "bob",
    };
    
  • Omit<T,K> T타입에서 K프로퍼티를 제외하고 사용합니다.

    interface User {
        id: number;
        name: string;
        age: number;
        gender: "M" | "W";
    }
      
    const admin: Omit<User, 'age' | 'gender'> = {
        id: 0,
        name: "bob",
    };
    
  • Exclude<T1,T2> Omit은 프로퍼티들을 제외하는 것이고 Exclude는 타입으로 제외를 합니다. T1중에서 T2와 겹치는것을 제외합니다.

    type T1 = string | number | boolean;
    type T2 = Exclude<T1, number | string> //boolean만 사용가능
    
  • NonNullable null을 제외한 타입을 생성합니다. undefined도 포함시켜서 제외시킵니다.

    type t1 = String | null | undefined | void;
    type t2 = NonNullable<t1>; //이렇게 해주면 string 과 void만 사용가능
    

태그:

카테고리:

업데이트:

댓글남기기