Generic

dev_sang·2022년 6월 18일
0

TypeScript

목록 보기
7/7

https://velog.velcdn.com/images/sanglee/post/c0bea2f5-a93d-4b6c-b269-3662afd47608/image.png

이 포스팅은 [DreamCoding 엘리님의 타입스크립트 강의](https://academy.dream-
coding.com/courses/typescript)을 복습하며 작성되었습니다.


Generic Function


아래 코드는 number 또는 null타입의 인자를 받고, null인 경우 에러메세지를 출력하고 number 타입인 경우 값을 그대로 리턴하는 함수입니다!

function checkNotNullNumber(arg: number | null): number {
  if (arg == null) {
    throw new Error("not valid type!");
  }
  return arg;
}

그런데 여기서 number타입 말고 다른 타입의 값들도 인자로 받아 검사하고 싶으면 어떻게 할까요?


아래 코드는 any타입 또는 null을 인자로 받을 수 있는 함수입니다.
any타입이니 number타입 외에 다른 타입들도 입력받을수 있겠죠?

function checkNotNullAny(arg: any | null): any {
  if (arg == null) {
    throw new Error("not valid number!");
  }
  return arg;
}

그런데 문제가 있습니다❗️ 이 함수의 리턴값도 any타입이라는 것이죠.
아래 변수 result에는 어떤 타입의 값도 올 수 있습니다.
어떤 값이 들어오든 타입에러가 나지 않고 타입 보장이 안되는 문제가 발생합니다!

let result = checkNotNullAny(123);
result = checkNotNullAny("abc");
result = true;

그렇다면 어떤 타입이든 인자로 받을 수 있으면서 리턴 후에도 타입을 보장하며 안전하게 사용할 수 있는 방법은 없을까요? 이럴 때 제네릭 사용합니다!


아래 코드의 함수는 인자로 들어온 값의 타입대로 리턴 후 타입도 결정됩니다.
arg에 boolean타입이 들어오면 리턴 후에도 arg값은 boolean타입인 것이죠 💡

function checkNotNull<T>(arg: T | null): T {
  if (arg == null) {
    throw new Error("not valid type!");
  }
  return arg;
}

resutl1의 경우를 보면, checkNotNull함수의 인자로 number타입이 들어갔으니 변수 result1의 타입도 number가 될 것입니다. 이때 result1에 문자열 값을 재할당 하려고 하면 Type Error가 발생하는 것이죠!

const result1: number = checkNotNull(123);
result1 = "abc"; // Type Error
const result2: boolean = checkNotNull(true);
const result3: boolean = checkNotNull("hi"); // Type Error

이렇게 Generic을 활용하면 타입을 보장하며 안전하게 사용할 수 있습니다.



Interface, Class

이번에는 인자 두개를 받아서 그대로 다시 리턴하는 클래스를 만들어보겠습니다.


먼저 Interface를 Generic으로 구현했습니다.
Interface Either는 left, right 메서드를 갖고 있습니다.
그리고 각각 L과 R을 리턴합니다.

interface Either<L, R> {
  // interface를 제네릭으로 구현
  left: () => L;
  right: () => R;
}

아래 코드의 Generic으로 만든 class인 SimpleEither은 Interface Either을 상속받습니다.
멤버변수 leftValue와 rightValue은 각각 generic타입 L, R인을 자신의 타입으로 갖습니다. 메서드 left, right는 두 멤버변수를 그대로 리턴합니다.

class SimpleEither<L, R> implements Either<L, R> {
  constructor(private leftValue: L, private rightValue: R) {}

  left(): L {
    return this.leftValue;
  }
  right(): R {
    return this.rightValue;
  }
}

인스턴스 example1을 생성합니다.
멤버 변수로 들어온 값의 타입(number)대로 리턴됩니다.

const example1: Either<number, number> = new SimpleEither(4, 5);
example1.left(); // 4
example1.right(); // 5

인스턴스 example2를 생성합니다.
: Either<number, string> 이렇게 타입 명시하는 부분을 생략할 수 있습니다!

const example2: Either = new SimpleEither(3, "hello");
example2.left(); // 3
example2.right(); // hello

인자로 들어가는 값과 다른 타입을 명시하면 타입에러가 납니다.

const example3: Either<number, boolean> = new SimpleEither(3, "hello");
// --> Error: Type 'SimpleEither<number, string>' is not assignable to type 'Either<number, boolean>'. The types returned by 'right()' are incompatible between these types. Type 'string' is not assignable to type 'boolean'
profile
There is no reason for not trying.

0개의 댓글