타입스크립트 다형성과 Generics type

Jaejun Kim·2023년 1월 10일
0

타입스크립트

목록 보기
8/9

다형성

/* Poly(many) morphism(forms) : 다형성
여러타입의 배열을 갖는 함수에 대해서


여러가지 다른 구조, 형태들

을 가지는 것은? 함수. 

파라미터를 2개, 3개 갖는 것
파라미터로 기본자료형이나 객체를 받는 것 
*/

// 예시로 배열을 받아서 forEach로 콘솔찍는 함수를 만들어보자. 
// 배열에 여러가지 자료형이 들어갈 수 있으니 오버로딩을 열심히 만들어주어야 할 것이다.
type SuperPrint = {
  (arr: number[]): void;
  (arr: boolean[]): void
  (arr: string[]): void
}

let superPrint: SuperPrint = (arr) => {
  arr.forEach(i => console.log(i))
}

superPrint([1, 2, 3, 4])
superPrint([true, false, true, false])
superPrint(['1', '2', '3', '4'])

// 근데 이러면 조금 복잡한 함수의 경우 세가지 모두를 call signature 해야할테고, 
// 최악의 경우로 만약 여러 타입이 섞인 값이 필요하다면? 
superPrint(['hi', false, 1])

// (arr: (string|boolean|number)[]): void 이런걸 더 만들어서 넣어야 할 것이다.
// 이는 변경할 수 없는 타입이고 이미 지정해둔 타입이다. 이를 콘트리트 타입 이라고 한다.

// 만약 마지막 경우처럼 드러올 타입이 확실하지 않을경우 이 대신 generic type을 쓸 수 있다. 

// typescript는 generic 타입을 <> (place-holder) 로 표시한다. 

type DuperPrint = {
  <Anithing>(arr: Anithing[]): void
  // <> (place-holder) 를 사용해 파라미터를 표기하고, 이를 type 부분에 놓는다. 
  // 파라미터에서 직접 타입을 추론하고, call signetures를 작성하겠다고 표현하는 것과 마찬가지다.
}
let duperPrint: DuperPrint = (arr) => {
  arr.forEach(i => console.log(i))
}

duperPrint([1, 2, 3, 4])
duperPrint([true, false, true, false])
duperPrint(['1', '2', 3, '4']) // <string | number>(arr: (string | number)[]) => void
duperPrint(['hi', false, 1]) // <string | number | boolean>(arr: (string | number | boolean)[]) => void


// 반환값에도 generic 타입을 사용할 수 있다. 
type SuperDuperPrint = {
  <T>(arr: T[]): T // 보통 T 라고 많이들 적는다.
}
let superDuperPrint: SuperDuperPrint = arr => arr[0]

superDuperPrint(['hi', false, 1]) // <string | number | boolean>(arr: (string | number | boolean)[]) => string | number | boolean


// generic 두개를 넣을 수 있다. 
type SuperPrint1 = <T, M>(a: T[], b: M) => T

let superPrint1: SuperPrint1 = (a, b) => {
  return a[0]
}

let aa = superPrint1([1, 2, 3, 4], 1)
let bb = superPrint1([true, false, true, false], 1)
let cc = superPrint1(['1', '2', 3, '4'], 1)
let dd = superPrint1(['hi', false, 1], '1') 

그래서 보통 어떻게 씀?

// 더 간단하게 제네릭을 쓸 수 있을까 ? 

type SuperPrint2 = <T>(a: T[]) => T
// 이렇게 쓰지 않고도 아래처럼 표현이 가능하다.

let superPrint2 = <T>(a: T[]) => {
  return a[0]
}

let aaaa = superPrint2<boolean>([1, 2, 3, 4]) // Type 'number' is not assignable to type 'boolean'.
// 이곳의 generic은 이제 boolean 이라는 뜻. 이렇게 임의로 정해줄 수 있다.

// 하지만 웬만해서는 스스로 타입스크립트가 추론하도록 내버려 두는것을 추천한다. 
let bbbb = superPrint2<boolean>([true, false, true, false])
let cccc = superPrint2(['1', '2', 3, '4'])
let dddd = superPrint2(['hi', false, 1])


// 제네릭 별칭Alias 에 사용하기 

type Players<E> = { // E 대신 아무거나 사용해도 된다.
  name: string
  extraInfo: E
} // E 라는 제네릭 타입을 선언하고 타입이 바뀔 수 있는 별칭에 지정할 수 있다. 


// 타입 내부에 타입을 또 지정해서 넣어주기. Alias 별칭은 type에서 지정한 속성을 써야하기 때문에 중요함 
type favFood = {
  favFood: string
}// extraInfo가 문자열이 아닌 객체라면 객체용 별칭을 또 만들어서 합쳐주면 된다. 아래처럼. 

type PlayerFavFood = Players<favFood>

const Jaejun: PlayerFavFood = {
  name: "jaejun",
  extraInfo: {
    favFood: "kimchi"
  }
}
// extraInfo 라는 타입에 generic 속성만이 있었기 때문에 다른값도 들어올 수 있었지만, 
//객체, 그것도 별칭을 정해줌으로서 타입을 더 명확하게 지정해줌

// generic이기 때문에 extraInfo에 null값이 들어가는 정보도 넣을 수 있을거임 
const Taehwan: Players<null> = {
  name: "Taehwan",
  extraInfo: null
} 

// 타입이 달라질 수 있을 때 쓸 수 있는것이 제네릭릭릭,

// 그냥 일반 타입 대신 쓸 수도 있음 
// 아래 세개는 똑같은 타입임. 
type A = Array<number>

function printAllNumbers(arr: A) {
  return arr
}
// ===
function printAllNumbers1(arr: Array<number>) {
  return arr
}
// ===
function printAllNumbers2(arr: number[]) {
  return arr
}

배움출처: https://nomadcoders.co/typescript-for-beginners/lobby

0개의 댓글