type Add = (a:number, b:number) => number; //call signature
type Add = { (a:number, b:number) //매개변수 : number//리턴값 } //더 길게 쓰는 방법
const add:Add = (a,b) => a+b //call signature에서 정의한 것과 같은 타입이 들어간다
function add(a : number , b : number) //:number 필요 없음 (당연히 리턴 값이 number니까)//{
return a+b
}
const add = (a:number, b:number) => a+b
타입을 직접 안 쓰고 싶다면?
//
type Add = (a:number, b:number) => number;
const add:Add = (a,b) => a+b //위와 같은 형태로 타입이 들어간다
const add:Add = (a,b) => {a+b} //리턴 값이 객체가 되어 잘못 사용한 거니까 리턴값의 타입이 void라고 알려준다
type Add = (a:number, b:number) => number; //call signature
type Add = {
(a:number, b:number):number
} //더 길게 쓰는 방법
=> 이 둘이 혼용될 수 있는 이유 : 오버로딩??
type Add = {
(a:number, b:number):number
(a:number, b:string):number //b는 number또는 string
} // 이렇게 call signature가 여러 개 있으면 오버로딩
const add : Add = (a,b) => a+b //error : b가 string일수도 있으니까
const add : Add = (a,b) => {
if(typeof b === "string") return a
return a+b //"number"면
} //이건 완전 비추
//Next.js에서는 아래 두개가 다 가능하다 : 오버로딩의 예시
type Config = {
path:string,
state:object
}
type Push = {
(path:string):void //바로 들어오면 path로
(config: Config):void //객체 형태 안에 path, state 속성이 있으면 config로
}
const push:Push = (config) => {
if(typeof config === "string"){ console.log(config) }
else {
console.log(config.path, config.state)
}
}
//이렇게 다 다른 call signature를 가지고 있는 함수 : 오버로딩이 발생
Router.push({
path : '/home'
state: 1
} )
Router.push('/home')
만약 매개변수의 수가 다른 call signature가 여러 개 있다면??
type Add = {
(a:number, b:number) : number
(a:number, b:number, c:number) : number
}
const add:Add = (a,b,c) =>{
//두개의 값만 사용하더라도 error
//두개의 파라미터만 넣어도 동작하므로 나머지 한 파라미터가 optional이라는 것을 명시해줘야 한다
return a
}//error
type Add = {
(a:number, b:number) : number
(a:number, b:number, c:number) : number
}
const add:Add = (a,b,c?:number) =>{
//이렇게 optional한 타입이라는 것을 명시해줘야 한다
if(c)return a+b+c
return a+b
}//error
다형성
poly : many, several, much, multi etc.
morphos : form, structure, shape etc.
a function that has diff shapes, forms
ex. 다양한 개수의 파라미터, 파라미터의 타입이 여러개 올 수 있을 때
예제 : 배열의 요소를 하나씩 출력하는 것
- 타입을 미리 주지 않고 placeholder를 주는 방법
- 사용할 때 call signature를 정할 수 있는 방법
- '제네릭은 선언 시점이 아니라 생성 시점에 타입을 명시하여 하나의 타입만이 아닌 다양한 타입을 사용할 수 있도록 하는 기법이다.'
type SuperPrint = {
<TypePlaceholder>(arr : TypePlaceholder[]) => TypePlaceholder
}
const hello : SuperPrint = (arr) => arr[0]
const a = hello([1,2,3])
//<number>(arr : number[]) => number
const b = hello(['1',2,true])
//<number | string | boolean>(arr:number | string | boolean[]) => number | string | boolean
제네릭 2개를 추가하고 싶을 때
type SuperPrint = <T, M>(a :T[], b:M) => T
superPrint([1,2,3,4], "x") //첫번째 원소의 타입을 리턴한다
cf. 그럼 any와다른 게 없지 않나?
any를 쓰면 나중에 타입과 무관한 메서드를 쓸 수 있다
type SuperPrint = (arr : any[]) => any
ex. 위의 경우에 a.toUpperCase()를 할 수 있게 된다. a의 타입이 후에 정해지는 게 아니라 계속 any니까
type SuperPrint = {
(arr : number[]): void //리턴하는 값이 없는 함수
(arr : boolean[]): void
}
const superPrint : SuperPrint = (arr) => {
arr.forEach(i => console.log(i))
}
superPrint([1,2,3,4])// 가능
superPrint([true, false, true])//가능
superPrint(["a","b","c"])//불가능
이렇게 정하지 않은 타입들을 사용하거나, 다양한 타입들이 섞여 있는 배열을 가능하게 하려면 어떻게 해야 할까?
-> generic을 사용한다!
: placeholder를 정해주고 타입스크립트가 타입을 추론할 수 있도록 한다
type SuperPrint = {
1. call signature 앞에 generic을 사용한다고 표현한다
<Generic>(arr : Generic[]):void
<T>(arr : T[]):void
<Potato>(arr : Potato[]):void
}
2. 타입스크립트의 요소를 확인하면서 타입을 정해준다 : Generic자리에 타입을 넣고 : 뒤에 넣는다
ex. <number>(arr : number[]):void
superPrint([1,2,3,4])// 가능
superPrint([true, false, true])// 가능
superPrint(["a","b","c"])// 가능
superPrint([1,2,true,false,"hello"]) //<number | bool | string>(arr : number | bool | string[]):void
3. 리턴 값의 타입도 Generic으로 해줄 수 있다
type SuperPrint = {
1. call signature 앞에 generic을 사용한다고 표현한다
<Generic>(a: Generic[]):Generic
}
const superPrint : SuperPrint = (arr) => arr[0]
const a = superPrint([1,2,3,4])// number
const b =superPrint([true, false, true])// bool
const c =superPrint(["a","b","c"])// string
const d =superPrint([1,2,true,false,"hello"]) //<number | bool | string>(arr : number | bool | string[]):number | bool | string
실제로는 제네릭을 이횽해서 직접 Call Signature를 만드는 일을 그렇게 많이 하지 않는다
대부분 다른 사람들이 만든 라이브러리들에서 이미 만들어진 형태의 Call Signature를 이용할 뿐
ex. React, Next 등
제네릭을 call signature 외에서 사용할 수 있는 방법
typescript가 타입을 추론할 수 있도록 한다
const a = superPrint<number>([1,2,3,4])
const a = superPrint([1,2,3,4])
//둘다 같다
type Player<E> = {
name:string
extraInfo:E
}
type NicoExtra = {
favFood:string
}
1)const nico:Player<{favFood:string}> = {
name:"nico",
extraInfo:{
favFood:"kimchi"
}
}
2)type NicoPlayer = Player<{favFood:string}>
const nico:NicoPlayer = {
name:"nico",
extraInfo:{
favFood:"kimchi"
}
}
3)type NicoPlayer = Player<NicoExtra>
const nico:NicoPlayer = {
name:"nico",
extraInfo:{
favFood:"kimchi"
}
}
//위의 세개가 같다
제네릭을 만들 수 있는 다른 방법
type arrNumbers = Array<number>
let a : A = [1,2,3,4]
function printAllNumbers(arr:Array<number>){}
function printAllNumbers(arr:number[]){}
//위의 두개가 같다
react + typescript
: useState<number>() //이렇게 사용