티스토리 뷰

call signatures || function signatures

함수 위에 마우스를 올렸을 때 보이는 것을 말한다. 함수를 어떻게 호출해야하고 반환 타입이 어떻게 되는지 알려준다.

const add = (a:number, b:number) => a + b

// call signatures
// const add : (a:number, b:number) => number

 

이렇게 콜시그니처를 만들 수 있다.

type Add = (a:number, b:number) => number

 

이렇게 타입을 설정해두고 함수에 적용할 수 있다.

type Add = (a:number, b:number) => number

const add:Add = (a, b) => a + b

 

 

오버로딩(overloading) - function overloading || method overloading

아까 적었던 콜시그니처를 이렇게도 적을 수 있다. 이는 오버로딩 때문인데 오버로딩은 한 함수가 서로 다른 여러개의 콜시그니처를 가질 때 발생한다. 오버로딩은 콜시그니처가 여러개 있는 함수라고 보면 된다.

type Add = {
  (a:number, b:number) : number
}

 

Next.js를 사용할 때 오버로딩이 발생할 수 있다.  자주 보이는 예시이다.

// Next.js를 사용할 때
Router.push({
  path: "/home",
  state: 1
})

Router.push("/home")

그럴 땐 이렇게 사용하면 된다.

type Config = {
  path: string,
  state: number
}

type Push = {
  (path: string): void
  (config: Config): void
}

const push: Push = (config) => {
  if(typeof config === 'string') {
    console.log(config)
  } else {
    console.log(config.path, config.state)
  }
}

 

파라미터의 갯수가 다를 수 있다. 이렇게만 적으면 타입스크립트가 에러메세지를 띄운다.

type Add = {
  (a: number, b: number) : number
  (a: number, b: number, c: number) : number
}

const add: Add = (a, b, c) => { // 여기서 타입스크립트가 에러메세지를 보여줌
  return a + b
}

그럴 땐 c는 옵셔널로 적어주면 된다.

type Add = {
  (a: number, b: number) : number
  (a: number, b: number, c: number) : number
}

const add:Add = (a, b, c?:number) => { // c는 옵션으로 적어줌
  if (c) return a + b + c
  return a + b
}

 

다형성(polymorphism)

글자 그대로 풀이하면 여러 다른 구조이다. 

 

배열을 받아서 출력해주는 함수를 만든다고 했을 때 이렇게 적어 줄 수 있다. 

type SuperPrint = {
  (arr: number[]):void
  (arr: boolean[]):void
  (arr: string[]):void
}

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

superPrint([1,2,3,4])
superPrint([true, false])
superPrint(["a", "b"])

 

그런데 이렇게 적으면 에러메시지를 보여준다. 그럴 때마다 타입에 모든 경우의 수를 생각해서 적어줄 수는 없다.

type SuperPrint = {
  (arr: number[]):void
  (arr: boolean[]):void
  (arr: string[]):void
}

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

superPrint([1,2,3,4])
superPrint([true, false])
superPrint(["a", "b"])
superPrint([1, 2, true, "c"]) // 에러메세지를 보여줌

 

제네릭(generic) 

그래서 어떤 concrete type이 들어올거라는 것은 아는데 정확히 어떤 타입이 들어올지는 모를 때 generic을 사용한다. 그럼 타입스크립트가 알아서 타입을 추론한다. <>를 사용해서 원하는 이름을 적어주고 사용하면 된다. 이름은 T를 많이 쓰이는 것 같다.

type SuperPrint = {
  <TypePlaceholder> (arr: TypePlaceholder[]):void
}

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

superPrint([1,2,3,4])
superPrint([true, false])
superPrint(["a", "b"])
superPrint([1, 2, true, "c"])

리턴값의 타입에도 사용할 수 있다.

type SuperPrint = {
  <T> (arr: T[]):T
}

 

그럼 any를 쓰면 되지 않나라고 생각할 수 있지만 any를 쓰면 타입과 다른 메소드를 사용해 오류가 날 때 타입스크립트가 잡아내지 않는다. 하지만 any가 아니라 제네릭을 사용하면 실행하기 전에 에러메세지를 띄운다.

// a의 타입이 any일 때
// a는 1인데 문자열 메소드를 사용하면 에러가 남 -> 하지만 타입스크립트는 잡아내지 않음
a.toUpperCase()

 

제네릭을 여러 개 쓰고 싶을 때는 이렇게 하면 된다. 

type SuperPrint = {
  <T, M>(a: T[], b: M): T;
};

 

하지만 주로 다른 패키지나 라이브러리를 사용한다면 대부분 제네릭을 사용해서 콜시그니처를 직접 작성할 일은 잘 없다.

 

제네릭을 이렇게 좀 더 간단하게 쓰는 방법도 있다.

const superPrint = <T>(a: T[]) => {
  arr.forEach((i) => console.log(i));
};

superPrint([1, 2, 3, 4]);
superPrint([true, false]);
superPrint(['a', 'b']);
superPrint([1, 2, true, 'c']);

 

제네릭은 상속도 가능하다.

type Player<E> = {
  name: string;
  extraInfo: E;
};

type ExtraInfo = { favFood: string }

type BongPlayer = Player<ExtraInfo>;

const bong: BongPlayer = {
  name: 'bong',
  extraInfo: {
    favFood: 'tomato',
  },
};
728x90
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함