
Call Signature
우리가 타입스크립트에게 이 함수가 어떻게 호출되는지 설명해주는 부분이다.
parameter의 type는 무엇인지, function의 return type는 무엇인지 정의한다.
function add(a:number, b: number): number {
return a+b;
}
const add = (a:number, b:number) => a+b;
type Add = (a:number, b:number) => number;
// 함수의 call signature 타입을 만든다.
const add:Add = (a, b) => a + b
overloading
'과적하다, 너무 많이 적재하다'라는 뜻이다.
메소드의 이름이 같고, 매겨변수 parameter의 개수나 타입이 다른 경우.
여러개의 call signatures가 있는 함수
함수가 여러개의 call signatures를 가지고 있을 때 발생시킨다.
// Bad Example, overloading 설명하기 위함.
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
}
// Example, Next.js에서 Router.push할 때 overloading 많이 발생함
type Config = {
path: string,
state: object
}
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)
}
}
// Example, parameter 갯수 다른 경우
type Add = {
(a: number, b: number): number,
(a: number, b: number, c: number): number,
}
const add: Add = (a, b, c?:number) => {
if(c) return a + b + c
return a + b
}
add(1, 2)
add(1, 2, 3)
poly: many, several, many, multi
morphos: form, structure, shape
-> polymorphos: many diffrent structures/shapes
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, true])
superPrint(["a", "b", "c"])
superPrint([1, 2, true, false]) // 이건 안된다.
concrete type
string, number, void, object 등
generic type
타입스크립트에게 타입을 유추하도록 알려준다.
타입의 placeholder 같은거다.
call signature를 작성할 때, 함수에 들어올 확실한 타입을 모를 때 generic을 사용한다.<T>를 보통 사용한다.
타입스크립트는 placeholder에서 알아낸 타입으로 대체해준다.
// 함수의 call signature를 입력할 때, placeholder를 사용했다.
// 이게 polymorphism이다. superPrint는 다양한 shape를 가지고 있다.
type SuperPrint = {
<TypePlaceholder>(arr: TypePlaceholder[]): TypePlaceholder
}
// 보통 <T>를 사용해 '<T>(arr: T[]): T'로 표현한다.
const superPrint: SuperPrint = (arr) => arr[0]
superPrint([1, 2, true, false])
any를 사용하면 되는 거 아닌가?
아님. generic을 사용할 때, 타입스크립트가 이 함수의 call signature를 만들어주기 때문이다. generic은 내가 요구한대로 signature를 생성해줄 수 있는 도구이다.
generic을 하나 더 추가 하고 싶다면?
//ts는 generic을 처음 인식했을 때와 generic의 순서를 기반으로 generic의 type를 알게 된다.
type SuperPrint = {
<T, M>(a: T[], b: M): T
}
const superPrint: SuperPrint = (arr) => arr[0]
superPrint([1, 2, true, false], "x")
generic을 사용해서 직접 call signature를 만들일은 거의 없다. 라이브러리를 만들거나, 다른 개발자가 사용할 기능을 개발하는 경우에는 유용하다.
nextJs, nestJS, reactJS를 사용하면, generic을 사용하게 될 거고, ts에게 generic을 보내게 될 거다. generic을 사용하는 call signature를 작성하게 될 거다.
function superPrint<V>(a: V[]){
return a[0]
}
const a = superPrint<number>([1,2,3,4])
generic을 사용해서 타입의 재사용을 할 수 있다.
type Player<E> = {
name: string
extraInfo: E
}
type NicoExtra = {
favFood: string
}
type NicoPlayer = Player<NicoExtra>
const nico: NicoPlayer = {
name: 'nico',
extraInfo: {
favFood: 'kimchi'
}
}
const lynn: Player<null> = {
name: "lynn",
extraInfo: null
}
generics는 다양하게 많이 쓰인다.
const arr: Array<number> = [1,2,3,4]
useState<number()