[TypeScript] 함수, 인터페이스, 이넘, 제네릭

wonyu·2022년 6월 10일
0

typescript

목록 보기
2/3

함수

  • 함수를 선언할 때 매개변수반환 값에 타입을 작성

  • 아무것도 반환하지 않을 경우에는 반환 값에 void 타입 작성

    function sum(a: number, b: number): number {
    	return a + b;
    }
  • 선택적 매개변수

    • 타입스크립트는 함수에 정의된 모든 매개변수가 함수에 필요하다고 가정함
    • 따라서 필수가 아닌 매개변수는 선택적 매개변수로 작성
      단, 선택적 매개변수는 필수 매개변수보다 앞에 올 수 없음
  • 기본 매개변수

    • 매개변수의 기본 값을 통해 타입 추론을 하여 타입을 알게 되므로, 타입을 작성하지 않아도 됨
  • Rest 문법 적용 예시
    • 개인적으로 헷갈렸던 부분.. spread & rest 참고: spread와 rest

      function sum(a: number, ...nums: number[]): number {
        let total = 0;
        for (let key in nums) {
          total += nums[key];
        }
        return a + total;
      }

인터페이스

  • 보통 다음과 같은 범주에 대해 정의할 수 있다.

    • 객체의 스펙 (속성, 속성의 타입)
    • 함수의 파라미터
    • 함수의 스펙 (파라미터, 반환 타입 등)
    • 배열과 객체를 접근하는 방식
    • 클래스
  • 예시

    // 인터페이스를 적용하지 않을 경우
    let person = { name: 'YJ', age: 20 };
    
    function logAge(obj: { age: number }) {
    	console.log(obj.age);
    }
    logAge(person);
    // 인터페이스를 적용할 경우
    interface personAge {
    	age: number;
    }
    
    // 인자를 더 명시적으로 표시할 수 있음
    function logAge(obj: personAge) {
    	console.log(obj.age);
    }
    let person = { name: 'YJ', age: 20 };
    logAge(person);
    • 인터페이스를 인자로 받아서 사용할 때, 객체의 속성 개수와 인터페이스의 속성 개수가 같지 않아도 된다.
      위 코드를 보면 personAge 인터페이스는 age 1개의 속성만 갖지만 personname , age 총 2개의 속성을 가져도 상관 없다. 또한 여러 개의 속성일 경우 순서도 상관 없다.
      interface Student {
        readonly studentId: number;
        studentName: string;
        age?: number;
        gender: string;
        courseCompleted: boolean;
        // here
        addComment(comment: string): string;
        addComment: (comment: string) => string;
      }
    • 메서드는 위와 같이 2개의 방식으로 정의할 수 있다.

옵션 속성

  • 옵션 속성일 경우 물음표를 붙인다.
    interface 인터페이스_이름 {
    	속성?: 타입;
    }

읽기 전용 속성

  • 객체를 처음 생성할 때만 값을 할당할 수 있는 속성
  • readonly 를 붙인다.
    interface CraftBeer {
    	readonly brand: string;
    }
    
    let myBeer: CraftBeer = {
    	brand: 'Belgian Monk';
    }
    myBeer.brand = 'Korean Carpenter';  // error

읽기 전용 배열

  • 배열을 선언할 때 ReadonlyArray<T> 타입을 사용한다.
    let arrr: ReadonlyArray<number> = [1, 2, 3];
    
    arr.splice(0, 1);  // error
    arr.push(4);  // error

인터페이스로 함수 타입 정의

interface login {
	(username: string, password: string): boolean;
}

let loginUser: login;
loginUser = function(id: string, pw: string) {
	console.log('로그인 했습니다');
	return true;
}

인터페이스로 클래스 타입 정의

interface CraftBeer {
	beerName: string;
	nameBeer(beer: string): void;
}

class myBeer implements CraftBeer {
	beerName: string = 'Baby Guiness';
	nameBeer(b: string) {
		this.beerName = b;
	}
	constructor() {}
}

인터페이스 확장

interface Person {
  name: string;
}

interface Developer extends Person {
  skill: string;
}

let fe = {} as Developer;
fe.name = 'josh';
fe.skill = 'TypeScript';

Enum (열거형)

  • 값을 제한하고 싶을 때, 연관된 아이템들을 묶어서 표현할 수 있다.

숫자형 이넘

  • auto-incrementing이 특징이다.
    enum Direction {
    	Up,  // 0
    	Down,  // 1
    	Left = 5,  // 5
    	Right  // 6
    }
  • 사용 예시
    enum Res {
    	No = 0,
    	Yes = 1
    }
    
    function respond(recipient: string, message: Res): void {
    	console.log(`recipient is ${recipient}, message is ${message}`);
    }
    
    respond('YJ', Res.Yes);
  • Reverse Mapping
    • 숫자형 이넘에만 존재

      enum Enum {
        A, B, C
      }
      
      let a = Enum.A;  // 키로 값을 획득
      let keyName = Enum[a];  // 값으로 키를 획득
      console.log(`a: ${a} , keyName: ${keyName}`);  // a: 0 , keyName: A

문자형 이넘

  • 이넘은 기본적으로 숫자형이다. 이를 문자형 이넘으로 변경할 수 있다.
  • 문자형 이넘의 경우 auto-incrementing이 없으므로 전부 특정 값으로 초기화해주어야 한다.
    enum Direction {
    	Up = "UP",
    	Down = "DOWN",
    	Left = "LEFT",
    	Right = "RIGHT"
    }
  • 값을 제한하고자 할 때, 리터럴 타입을 이용하면 좀 더 쉽게 작성할 수 있다.
    interface Location {
    	direction: "UP" | "DOWN" | "LEFT" | "RIGHT"
    }

연산자를 이용한 타입 정의

Union Type

  • 제한된 타입들을 동시에 지정하고 싶을 때 사용

  • | 연산자를 이용해서 타입을 여러 개 연결하는 방식

    // text의 타입은 string이거나 number이다.
    function logText(text: string | number) {
    	// ...
    }
  • 단순히 OR 라고 생각하는 건 주의해야 한다.

    아래 예시에서 함수의 파라미터는 Person 이 될 수도, Developer 가 될 수도 있다. 하지만 타입스크립트의 입장에서는 introduce() 함수를 호출할 때 어느 타입이 올지 알 수 없다. 따라서 어느 타입이 오든 에러가 나지 않는 방식으로 추론하게 되기 때문에, 특정 타입에만 있는 속성을 사용하면 오류가 발생한다.

  • 이러한 경우에 typeof/in/instanceof 등의 operator와 조건문을 사용해서 문제를 해결할 수 있다.

  • 이와 같이 타입을 narrow down할 수 있게 해주는 것을 타입 가드라고 한다.

Intersection Type

  • & 연산자를 이용해서 하나의 타입이 여러 타입을 모두 만족하도록 함
    interface Person {
      name: string;
      age: number;
    }
    
    interface Dev {
      name: string;
      skill: number;
    }
    
    type Capt = Person & Dev;
    let capt: Capt = {
      name: 'capt',
      age: 20,
      skill: 5,
    }

제네릭

  • 타입을 함수의 파라미터처럼 사용하는 것
  • 선언 시점이 아니라 생성 시점에 타입을 명시 → 하나의 타입만이 아닌 다양한 타입을 사용할 수 있도록 하는 기법
  • 한 번의 선언으로 다양한 타입에 재사용 가능
    function getText<T>(text: T): T {
    	return text;
    }
    
    getText<string>('hi');
    getText('hi');  // 이렇게도 가능
    getText<number>(10);
    getText<boolean>(true);
    • 이 때 함수에서 any 를 사용한다면 함수가 동작은 하겠지만, 어떤 타입이 들어오고 어떤 타입이 반환되는지 알 수 없다. 이 문제를 해결할 수 있는 것이 제네릭이다.

제네릭 타입 변수

function logText<T>(text: Array<T>): Array<T> {
  // text를 그냥 T 타입으로 하면 length 속성이 없다는 에러가 뜸.
	// 이 때 Array<T>로 해서 제네릭에 타입을 줄 수 있음
  console.log(text.length);
  return text;
}

제네릭 제약 조건

  • extends 를 통해 제약을 주는 방법
    interface LengthWise {
      length: number;
    }
    
    function logText<T extends LengthWise>(text: T): T {
      console.log(text.length);
      return text;
    }
    
    logText('hi');
  • keyof 를 사용해서 객체의 속성을 제약하는 방법


0개의 댓글