-> 코드에 마우스 올리면 나오는 것
해당 코드의 변수나 함수의 타입을 알려줌!
타입 지정하지 않아도 타입스크립트가 해당 코드의 타입을 추론해서 적용해줌
함수가 만들어지기 이전에 어떻게 작동하는지 서술한다고 할 수 있음
프로그램을 짜기 전에 타입을 먼저 생각하고 코드를 구현하는 방식에서 많이 쓰임
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 생성자를 통해 만들 수 있음!
함수가 서로 다른 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 }
- concrete type => 기존의 존재하는 타입(string, boolean, number ...)
- generic -> 타입의 placeholder
- 타입스크립트가 추후에 해당 타입을 추론해서 자동으로 타입을 인식하도록 해줌!
-> 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에 모든 경우를 적지 않아도됨!
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 활용 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은 패키지나 라이브러리의 구조를 만들고 디자인할 때 많이 사용한다!