07.TS Generic

Updated:


TS Generic

1.Generics, Any 와 다른점

  • 타입을 다 받기 위해 Any 를 사용하게 되면 아래의 코드와 같이 error 가 발생 됩니다.
function helloString(message: string): string {
  return message;
}

function helloNumber(message: number): number {
  return message;
}

// 위와 같이 반복적인 함수들 이 발생되는데.. 그래서 범용적으로 사용하기 위해서 any 를 사용할 때,

// any 를 사용하게 되면 발생되는 문제

function hello(message: any): any {
  return message;
}

// 출력은 잘 되지만 추가로 매서드를 사용할 때, error 가 발생됨
console.log(hello("Jacob"));
console.log(hello(80));

// error (str 에 사용할 수 있는 .length 를 hello() 가 any 이기 때문에 사용할 수 없음)
console.log(hello("Jacob").length);

🔶 그래서 Generic 을 사용하게 됨으로 위와 같은 문제를 해결 할 수 있습니다.

function helloGeneric<T>(message: T): T {
  return message;
}

//  message 가 'Jacob' 이라는 literal 형식이 그냥 type 이 되버리는것  -> Generic
console.log(helloGeneric("Jacob"));

// 그렇기 때문에 추가로 .method 사용이 가능해 짐 (any 와 다른 점!!)
console.log(helloGeneric("Jacob").length);

// 이번엔 type 이 number 형태인 80 이 되기 때문에 .숫자관련 method 를 사용할 수 있음
console.log(helloGeneric(80));

// 타입이 true 라는 boolean 타입으로 사용할 수 있음.
console.log(helloGeneric(true));
  • Generic 장점 : 타입으로 된 연산이 내부 함수 내에서 사용이 가능하게 됨 (any 와 다른점임 !!)

2.Generic Basic

function helloBasic<T>(message: T): T {
  return message;
}

// 직접 generic 에 type 을 지정해서 사용하는 방법 -> 넣고 사용하면 뒤에값이 명시된 type 과 같아야 하고,
helloBasic<string>("Jacob");

// 위에 <T> 가 값을 추론해서 <T> 가 number 임을 추측해서 사용됨  -> 않널고 쓰면 뒤에 값에따라 알아서 추론되서 사용됨
helloBasic(80);

// generic 을 여러개 사용한 경우

function helloBasic2<T, U>(message: T, comment: U): T {
  return message;
}

// T 는 string 이라고 명시, U 는 number 임을 명사 그러면 뒤에는 string, number 형태의 값이 와야 함
helloBasic2<string, number>("Jacob", 80);

// 명시를 안하고 넣을때는 알아서 두개다 number 로 추론됨. T 와 U 는 number type 으로 추론되서 사용됨 (단, <T, U> 를 넣었기 때문에 반드시. 2개의 값으로 있어야 함)
helloBasic2(36, 39);

3.Generic array & tuple

//  generic array
function helloArray<T>(message: T[]): T {
  return message[0];
}

// 2개다 string 일 경우, 둘다 str 로 추론 되어 type 이 지정됨
helloArray(["Hello", "world"]);

//  자동으로 추론 되어 string | number 이렇게 union type 으로 지정됨
helloArray(["hello", 5]);

// generic tutple

function helloTuple<T, K>(message: [T, K]): T {
  return message[0];
}

// array 와 마찬가지로 둘 다 str 으로 받아서 사용됨
helloTuple(["Hello", "world"]);

// 근데 정확하게 0 번 째 index 가  str 으로 추론 됨에 따라 .length 같은 함수를 사용할 수 있음
helloTuple(["Hello", 5]);

4.Generic Function

// generic alias
type HelloFunctionGeneric1 = <T>(message: T) => T;

const helloFunction1: HelloFunctionGeneric1 = <T>(message: T): T => {
  return message;
};

// generic interface
interface HelloFunctionGeneric2 {
  <T>(message: T): T;
}

const helloFunction2: HelloFunctionGeneric2 = <T>(message: T): T => {
  return message;
};

// 기존의 alias, interface 형식에다가 그냥 generic 만 추가한 형태임

5.Generic class

// generic class

class Person<T, K> {
  private _name: T;
  private _age: K;

  constructor(name: T, age: K) {
    this._name = name;
    this._age = age;
  }
}

new Person("Jacob", 39);

// str 으로 지정하고 number 를 넣기 때문에 error
new Person<string>(39);

// T, K 를 str , number 로 지정했기 때문에 맞춰서 나와야 함
new Person<string, number>("Jacob", "age");

6.Generic with extends

  • class 에서 사용되는 extends 는 상속의 개념이지만 generic 에서는 좀 다른 의미를 자지고 있습니다.
// generic extends
// 특정 타입만 사용이 가능하다고 명시하는것임 즉, T 는 string 또는 number type 만이 될 수 있다
// Type 을 사용할때 , 가능한 가장 작은범위를 사용해서 이용하는 것이 나중에 컴파일 할때 error 를 줄일 수 있다.
// generic type 을 사용 할 때는 extends 키워드를 사용하는 것을 추천 (그나마, type 의 범위를 줄여서 사용 할 수 있기 때문)

class PersonExtends<T extends string | number> {
  private _name: T;

  constructor(name: T) {
    this._name = name;
  }
}

// T 에 string 이 있으니까 ture
new PersonExtends("Jacob");

// T 에 number 가 있으니까 true
new PersonExtends(80);

// T 에 boolean 이 없으니까 false
new PersonExtends(true);

7.keyof & type lookup system

  • keyof 와 generic 을 이용해서 type 을 찾아내는 시스템을 만드는것 (타입을 정확하게 찾아내는 방식임)
interface IPerson {
  name: string;
  age: number;
}

const person: IPerson = {
  name: "Jacob",
  age: 80,
};

// keyof 타입 앞에 붙여서 새로운 타입을 만들어 내는 것임
type Keys = keyof IPerson;

const Keys: Keys = "age";

// IPerson[keyof Iperson]
// => Iperson["name" | "age"]
// => Iperson["name"] | Iperson["age"]
// => string | number
// generic 사용
// K는 keyof T 에 의 해 재한된 형태 라는 것임
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

getProp(person, "name");

function setProp<T, K extends keyof T>(obj: T, key: K, value: T[K]): void {
  obj[key] = value;
}

setProp(person, "name", "John");

🔶 🔷 📌 🔑

Reference

Leave a comment