
function add(a:number, b:number){
return a + b
}
const add = (a:number, b:number) => a + b
a: number, b: number의 number 타입을 작성하고 싶지 않고, 내가 작성한 add함수만의 타입을 만들고 싶을 때
User라는 타입과 Age라는 타입도 만들었던 것과 같이 함수에 대한 타입도 만들 수 있다
앞으로 작성할건 call signatures가 될 것이다
call signatures란?
ts 안내창 > 함수 위에 마우스를 올렸을 때 보게 되는 걸 말한다
예) const add: (a: number, b: number) => number 이걸 call signatures라고 한다
함수를 어떻게 호출해야하는 것인지 알려주고 인자의 타입과 함수의 반환 타입을 알려준다
// 함수의 call signatures를 만드는 방법
type Add = (a: number, b:number) => number;
const add:Add = (a,b) => a+b
overloading = function overloading = method overloading 이라고도 부른다
우리가 사용하는 라이브러리 같은 곳에서 overloading을 많이 사용하기 때문에 알아야한다
overloading이란?
함수가 여러개 즉, 서로 다른 여러 개의 call signatures를 가지고 있을 때 발생시킨다
다시 말하면, overloading은 여러 call signatures가 있는 함수라고 말할 수 있다
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; // b가 number이면, a+b를 리턴한다
}
예를 들어, Next.js의 router를 쓸때
Router.push({
path: "/home",
state: 1
})
Router.push("/home")
// 위의 object 또는 string으로 전달을 할 수 있다
// 이런 경우에 overloading을 어떻게 사용할 수 있을까?
// 일상생활에서 개발할 때 볼 수 있는 overloading
type Config = {
path: string,
state: object
}
type Push = {
// 이건 Router.push("/home")의 string을 의미
(path:string):void // void는 아무것도 리턴하지 않는 것을 의미
// 이건 Router.push({path: "/home", state: 1})의 string과 object를 의미
(config: Config):void // obj는 config obj이므로 위에 Config type을 만들어서 진행
}
const push: Push = (config) =>{ // 여기서 path나 config타입을 받을텐데, 이런 경우에는 config를 받고 아무것도 리턴하지 않는다. // 그리고 (config)에 마우스를 가져다 대면 string | Config를 받을 수 있다고 나온다
// 내부에서는 타입을 나눠서 체크하도록 진행해준다
if(typeof config === "string") console.log(config)
else console.log(config.path, config.state)
// return이 없는 이유는 위에서 Push타입엔 void로 아무것도 리턴하지 않는다고 설정해서이다
}
type Add = {
(a: number, b: number) : number
(a: number, b: number, c: number): number,
}
// 파라미터의 갯수가 다르기 때문에 c는 옵션같은 의미를 가진다
// Add를 부를 때, a, b를 부를 수도 있고, 또는 a, b, c를 부를 수도 있기 때문에
// 그렇기 때문에 c는 아마도 number일 것이라는 의미를 넣어줘야한다
// c라는 파라미터는 선택사항이라는 것을 알려준다는 말이다
const add: Add = (a, b, c?:number) =>{ // 파라미터 개수가 다를 때 c?:number처럼 넣어줘야한다
if(c) return a + b + c
return a + b
}
add(1, 2) // 3
add(1, 2, 3) // 6
여러가지 다른 형태들
기본적으로 함수는 여러가지 다른 모양을 가지거나 다른 형태를 가지고 있다
함수는 다른 2-3개의 parameter를 가질 수 있고, string이나 object를 첫번째 파라미터로 가질 수 있다
이러한 형태들도 polymorphism 다형성에 속한다
type AllPrint = {
(arr: number[]): void // 함수는 아무것도 리턴하지 않으니까
(arr: boolean[]): void
}
// 배열을 받고 그 배열의 요소를 하나씩 print해주는 함수
type allPrint : AllPrint = (arr) => {
arr.forEach(i => console.log(i))
}
allPrint([1, 2, 3, 4])
allPrint([true, false, false, true])
generic이란?
타입스크립트가 어떻게 다형성을 주는지를 의미한다
타입의 placeholder 같은 것을 말한다
concrete type을 사용하는 것 대신 쓸 수 있다
내가 요구한대로 call signatures를 생성해줄 수 있는 도구
number, string, boolean, void, unknwon 등 우리가 자주 접했던 타입들
call signature를 작성할 때, 들어올 타입을 확실하게 모를 때 generic을 사용한다
원래 코드를 작성하고 함수를 구현하고 사용할때는 concrete type을 사용해야한다
다만, call signature를 작성하는데 concrete type을 알 수 없을때도 분명히 있다
그런 경우 generic을 사용한다
즉, 어떤 타입들이 들어있는지 모르는 배열이 올 경우 직접 모든 call signature를 작성해주기 힘드니까 사용한다
(generic을 사용하면 ts는 내가 작성한 코드를 보고 타입을 알아낸다)
type AllPrint = {
// generic을 쓸때는 <>안에 내가 짓고 싶은 이름을 넣는다 보통 T,V를 많이 쓴다
<TypePlaceholder>(arr: TypePalceholder[]): void
}
type allPrint : AllPrint = (arr) => {
arr.forEach(i => console.log(i))
}
// 모두 정상적으로 작동
allPrint([1, 2, 3, 4])
allPrint([true, false, false, true])
allPrint(["a", "b", "c"])
allPrint([1, 2, true, false, "hello"])
type AllPrint = {
// 만약 allPrint함수에서 return값이 있고
// 그 return값 또한 어떠한 타입일지 정확히 모르니까 TypePlaceholder라고 해준다
<T>(arr: T[]): T
}
type allPrint : AllPrint = (arr) => arr[0] // 첫 번째 요소만 출력
// 모두 정상적으로 작동
const a = allPrint([1, 2, 3, 4]) // a는 number타입
const b = allPrint([true, false, false, true]) // b는 boolean타입
const c = allPrint(["a", "b", "c"]) // c는 string타입
const d = allPrint([1, 2, true, false, "hello"]) // d는 number or boolean or string 타입
type AllPrint = <T, M>(arr: T[], arr2: M) => T
const allPrint: AllPrint = (arr) => arr[0]
allPrint([1, 2, 3, 4], "x")
allPrint([true, false, "hello"], 2)
타입스크립트는 generic이 처음 사용되는 지점을 기반으로 이 타입이 무엇인지 알게 된다
type AllPrint = <T>(arr: T[]): T
type allPrint : AllPrint = (arr) => arr[0]
const a = allPrint([1, 2, 3, 4])
const b = allPrint([true, false, false, true])
const c = allPrint(["a", "b", "c"])
const d = allPrint([1, 2, true, false, "hello"])
// 일반 함수로 대체하는 방법
function allPrint<T>(arr: T[]){
return a[0]
}
allPrint([1, 2, 3, 4])
// allPrint<number>([1, 2, 3, 4])는 같다
type User<V> = {
name: string
extraInfo: E
}
const hong : User<{favFood}> = {
name: "hong",
extraInfo: {
favFood: "kimchi"
}
}
type User<V> = {
name: string
extraInfo: E
}
type HongUser = User<{favFood: string}>
const hong : HongUser = {
name: "hong",
extraInfo: {
favFood: "kimchi"
}
}
type User<V> = {
name: string
extraInfo: E
}
type HongExtra = {
favFood: string
}
type HongUser = User<HongExtra>
const hong : HongUser = {
name: "hong",
extraInfo: {
favFood: "kimchi"
}
}
// 많은 것들이 있는 큰 타입(User)를 하나 가지고 있는데, 그 안의 요소가 하나 달라질 수 있는 타입이라면 generic을 넣으면 된다!!!
const Kim: User<null> = {
name: "Kim",
extraInfo: null
}
// 1번
type A = Array<number>
let a: A = [1, 2, 3, 4]
// 2번
funtciont printAllNumbers(arr: Array<number>){}
useState<number>()