
함수의 인자(arguments) 타입과 반환 타입을 알려주는 것이다.
선언 방법
type Add = (a: number, b: number) => number
// or
type Add = {
(a: number, b: number): number
}
const add: Add = (a, b) => a + b b
- 오버로딩이란 이름은 같지만 파라미터 개수, 타입(시그니처)이 다른 함수를 중복으로 선언하는 것을 의미한다.
함수가 서로 다른 여러 개의 call signature를 가질 때 오버로딩이 발생한다.
즉, 오버로딩은 여러 call signature가 있는 함수를 말하는 것이다.
type Add = {
(a: number, b: number): number
(a: number, b: string): number
}
const add: Add = (a, b) => {
if (typeof b === 'string') return a
return a + b
}
여러 개의 argument를 가지고 있는 경우 (자주 사용하는 방법은 아님)
다른 개수의 파라미터를 가지게 되면, 나머지 파라미터의 타입을 지정해줘야 한다.
type Add = {
(a: number, b: number): number
(a: number, b: string, c: number): number // c파라미터는 optional함
}
const add: Add = (a, b, c?: number) => { // c가 선택사항이라는 것을 알려줘야함
if (c) return a + b + c
return a + b
}
add(1, 2)
add(1, 2, 3)
- 다형성이란?
- 'poly'는 그리스어로 많은, 다수의 란 뜻이다. 'morphos'는 형태, 구조 라는 뜻을 가지고있다.
이 둘을 조합하면 'many structure'. 즉, 여러가지 구조, 형태 라고 해석할 수 있다.
number, boolean, string, void, unknown 타입 등이 있다.
placeholder를 사용하여 작성한 코드의 타입 기준을 바꿔준다. concrete 타입을 사용하는 것 대신 사용할 수 있는 타입이다.
타입스크립트가 해당 타입을 유추할 수 있다.
Call Signature 를 작성할 때, 확실한 타입을 모르는 경우 generic 타입을 사용한다.
< T > : placeholder 를 사용하여 타입스크립트에게 generic 타입을 받고 싶다는 것을 표현하는 식
generic 타입을 이용하면 인수의 타입을 모두 정의해 줄 필요가 없다.
// generic 타입 적용 전
type SuperPrint = {
(arr: number | boolean | string[]): void
}
// generic 타입 적용 후
type SuperPrint = {
<TypePlaceholder>(arr: TypePlaceholder[]): void
}
const superPrint: SuperPrint = (arr): void => {
arr.forEach(i => console.log(i))
}
generic 타입을 사용한 Call Signagture 를 지정한 경우 타입스크립트가 추론한 타입으로 Call Signature가 대체된다.
예제1
type SuperPrint = {
<TypePlaceholder>(arr: TypePlaceholder[]): void
}
superPrint([1, 2, 3, 4])
// 타입스크립트가 추론하여 대체한 call signature
const superPrint: <number> = (arr: number[]): void => {
arr.forEach(i => console.log(i))
}
예제2
type SuperPrint = {
<TypePlaceholder>(arr: TypePlaceholder[]): void
}
superPrint([1, 2, true, 'a'])
// 타입스크립트가 추론하여 대체한 call signature
const superPrint: <number | boolean | string> = (arr: (number | boolean | string)[]): void => {
arr.forEach(i => console.log(i))
}
리턴 타입을 바꾸고 싶은 경우
type SuperPrint = <T>(arr: T[]) => T
const superPrint: SuperPrint = (arr) => arr[0]
const a = superPrint([1, 2, 3, 4])
const b = superPrint([true, true, false, false])
const c = superPrint(['a', 'b', 'c', 'd'])
const d = superPrint([1, 2, true, 'a'])
타입스크립트는 작성한 코드를 보고 타입을 유추하여 placeholer로 정의한 generic 타입을 대체한다.
any 와 generic 은 동일한 작업을 하지 않는다.
generic 은 우리가 하는 요청에 따라 call signature를 생성한다.
any는 어떠한 타입도 정의될 수 있다는 뜻이여서 타입스크립트가 보호해주지 않는다.
type SuperPrint = (arr: any[]) => any
const superPrint: SuperPrint = (arr) => arr[0]
const a = superPrint([1, 2, 3, 4])
const b = superPrint([true, true, false, false])
const c = superPrint(['a', 'b', 'c', 'd'])
const d = superPrint([1, 2, true, 'a'])
d.toUpperCase() // 라인 에러 발생 X
generic 은 타입스크립트에서 타입을 유추하여 대체하기 때문에 타입스크립트가 보호해 줄 수 있다.
type SuperPrint = <T>(arr: T[]) => T
const superPrint: SuperPrint = (arr) => arr[0]
const a = superPrint([1, 2, 3, 4])
const b = superPrint([true, true, false, false])
const c = superPrint(['a', 'b', 'c', 'd'])
const d = superPrint([1, 2, true, 'a'])
d.toUpperCase() // 라인 에러 발생
generic 은 사용자가 요구한대로 signature 를 생성해줄 수 있는 도구
여러 개의 generic 타입을 사용하고 싶은 경우
타입스크립트는 generic 을 처음으로 인식했을 때와 generic 의 순서를 기반으로 generic 의 타입을 유추하게 됨
type SuperPrint = <T, V>(a: T[], b: V) => T
const superPrint: SuperPrint = (arr) => arr[0]
const a = superPrint([1, 2, 3, 4], 'x')
const b = superPrint([true, true, false, false])
const c = superPrint(['a', 'b', 'c', 'd'])
const d = superPrint([1, 2, true, 'a'])
generic 타입은 실제 개발하는 경우 잘 사용하지 않는 타입이다.
다만 라이브러리를 만들거나, 다른 개발자가 사용할 기능을 개발하는 경우엔 유용하다.
Call signature 외에 도 다른 방법으로 사용가능하다.
대부분의 기본적인 타입스크립트의 타입은 generic 으로 만들어져있다.
const numArr: number[] = [1, 2, 3, 4]
const numArr: Array<number> = [1, 2, 3, 4]