[TypeScript] Call Signatures와 Over Loading

제론·2022년 6월 15일
2
post-thumbnail

call signatures

-> 코드에 마우스 올리면 나오는 것

  • 해당 코드의 변수나 함수의 타입을 알려줌!

  • 타입 지정하지 않아도 타입스크립트가 해당 코드의 타입을 추론해서 적용해줌

  • 함수가 만들어지기 이전에 어떻게 작동하는지 서술한다고 할 수 있음

  • 프로그램을 짜기 전에 타입을 먼저 생각하고 코드를 구현하는 방식에서 많이 쓰임

function add(a:number, b:number) {
    return a + b
}
  • 화살표 함수일 때 결과의 타입 추론함
    const add2 = (a:number, b: number)=> a + b
    - 인수 number 타입이므로 a + b의 결과의 타입은 number!
  • 함수 인수에 타입을 넣고싶지 않다면?!
    -> call signatures 사용

`const add3 = (a, b) => a + b

  • call signatures 만들기
    type Add = (a:number, b:number) => number
    - type 생성자를 통해 만들 수 있음!

오버로딩(over loading)

  • 함수가 서로 다른 call signatures를 가지고 있을 때 발생함

  • 외부 패키지나, 라이브러리 사용시 많이 사용됨

오버로딩

type Add2 = {
    (a: number, b: number): number
    (a: number, b: string): number
}

const add5: Add2 = (a, b) => {
    if(typeof b === 'string') return a
    return a + b
}

- Add2의 call signatures를 보면 b의 타입이 number 와 string 동시에 지정되어 있다.

오버로딩 예시 1

  • call signatrues에 파라미터 타입이 다른 경우
  • next.js에서 라우터의 경로를 설정할 때, stirng이나 객체를 넘겨줄 수 있음!
type Config = {
    path: string,
    state: object
}
type Push = {
    (path:string): void
    (config:Config): void
}
// Push 타입은 string과 object를 받을 수 있고 string일 때와 object일 때를 각각 처리할 수 있음
// 패키지 혹은 라이브러리를 처음에 디자인할 때 이런식으로 많이 사용됨
const push:Push = (config) => {
    if(typeof config === "string") console.log("do something")
    else {
        console.log(config.path)
    }
}

오버로딩 예시 2

  • call signatures에 파라미터 개수가 다른 경우
type Add3 = {
    (a: number, b: number): number
    (a: number, b: number, c: number): number
}
// c는 옵션(선택사항)이라는 표시로 ? 사인을 넣어줌!
// 세 번째 인수는 넣어도 동작하고 안넣어도 동작한다.
const add6:Add3 = (a, b, c?: number) => {
    return a + b
}

다형성(polymorphism)

직역 -> 여러가지 다른 형태들

  • concrete type => 기존의 존재하는 타입(string, boolean, number ...)
  • generic -> 타입의 placeholder
  • 타입스크립트가 추후에 해당 타입을 추론해서 자동으로 타입을 인식하도록 해줌!

왜 generic 사용?

-> call signature를 작성할 때, 들어올 값의 확실한 타입을 모를 때 사용!
=> 오버로딩을 해결할 수 있다!

  • 다형성을 사용하지 않을 경우
type SuperPrint = {
     (arr: number[]):void
     (arr: boolean[]):void
     (arr: string[]):void
     (arr: (number|boolean)[]):void
 }
  • generic 사용
// supePrint 함수는 많은 형태를 가지고 있음!
type SuperPrint = {
    <TypePlaceholder>(arr: TypePlaceholder[]):TypePlaceholder
}
// <>을 통해 call signature가 generic을 받는다는 것을 알려줌
// <>을 통해 타입스크립트에게 타입을 유추하도록 알려줌
// 이 예시는 generic을 사용하는 방법 중 하나
const superPrint: SuperPrint = (arr) => arr[0]

  • 타입스크립트는 들어온 데이터를 보고 타입을 추론해서 placeholder를 발견한 타입으로 바꿔준다.
  • 타입을 유추하여 모든 call signautres를 대체하고 다르게 바꿔줌
  • 각각의 타입이 다르게 return 되는 것을 알 수 있다.
const a = superPrint([1, 2, 3, 4])
const b = superPrint([true, false, true])
const c = superPrint(["a", "b", "c"])
const d = superPrint([1, 2, true, false, "hello"])

-> generic을 사용하면 call signatrues에 모든 경우를 적지 않아도됨!

generic은 1개 이상일 수 있다

  • 두 번째 인자로 또 하나의 generic을 만들고 함수의 인수를 넣음(개발자의 요청) -> 함수 호출시 TS가 타입 유추하여 알려줌
type SuperPrint2 = <T, M>(a: T[], b:M) => T
const superPrint2: SuperPrint2 = (a) => a[0]
const e = superPrint2([1, 2, 3, 4], '')
const f = superPrint2([true, false, true], 1)
const g = superPrint2(["a", "b", "c"], false)
const h = superPrint2([1, 2, true, false, "hello"], [])
e.toUpperCase() // 에러 발생 - any 대신 generic을 쓰는 이유!

=> 결국, generic은 개발자가 요구한대로 call signature를 생성해줄 수 있는 도구!

실제로 generic을 어디서 쓸까?

generic 활용 1

  • 함수로 만들어 사용
function superPrint3<V>(a: V[]) {
    return a[0]
}
const i = superPrint3<number>([1, 2, 3, 4]) // 직접 타입을 전달할 수도 가능 but 별로 안씀
const j = superPrint3([true, false, true])
const k = superPrint3(["a", "b", "c"])
const l = superPrint3([1, 2, true, false, "hello"])

generic 활용 2

  • 코드의 확장
type Player<P> = {
    name:string
    extraInfo: P
}
type ChanExtra = {
    favFood:string
}
type ChanPlayer = Player<ChanExtra>
const chan: ChanPlayer = {
    name: 'chan',
    extraInfo: {
        favFood: 'hambuger'
    }
}
  • 타입의 재사용
const jang:Player<null> = {
    name: 'jang',
    extraInfo: null
}
  • 기본적인 타입스크립트의 타입은 generic으로 돼있음!
type A = Array<number>
let x:A = [1, 2, 3, 4]
// 단순한 함수 생성에서도 많이 쓰임
function printAllNumbers(arr: Array<Number>) {}

react와 함께 사용
useState<number>()

generic은 패키지나 라이브러리의 구조를 만들고 디자인할 때 많이 사용한다!

profile
Software Developer

0개의 댓글