call signatures
:함수 위에 마우스를 올렸을 때 보게 되는 것, 함수가 어떻게 호출되는지 알려준다.
인자의 타입과 함수의 반환 타입을 알려주는 것

call signature 선언하기
type Add = (a: number, b:number) => number; //call signature 선언
const add: Add = (a, b) => a+b
이렇게 콜 시그니처를 선언해두고 사용하면 함수를 선언할 때 인자나 리턴 값이 타입을 말해줄 필요가 없다.
오버 로딩(overloading)
=function overloading
= method 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
}
이렇게 Add라는 한 타입 안에 서로 다른 콜 시그니처를 가지고 있을 땐 b의 타입에 따라 리턴 값이 달라질 수 있도록 b의 값을 먼저 확인해야 한다. 상황에 따라 보내주는 값이 달라지는 것.
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)
}
}
이렇게 인자 타입에 따라 다른 리턴 값을 가지게 되는 경우가 많이 생긴다고 한다!
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
}
이렇게 가지고 있는 파라미터의 개수가 다를 땐 c의 파라미터는 옵션처럼 여겨진다. 따라서 c 파라미터를 함수에서 사용할 땐 타입을 정의해주어야 한다. 따라서 'c는 선택사항인데 이 c 파라미터는 number일지도 모른다.'라는 뜻으로 c의 타입을 정의해주었다.
다형성(polymorphism)
:여러 가지의 구조
배열의 값에 어떤 타입이 들어와도 콘솔에 찍는 함수를 만들어보자
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, true]);
superPrint(["1", "2", "3"]);
이렇게 있는 경우 number타입 배열과 boolean 타입 배열은 정상 작동 하지만 string 설정해주지 않았기 때문에 오류가 난다. 그렇다면 string도 똑같이 추가해주는 것이 좋을까? 그러면 작동은 하지만 좋은 방법은 아니다. 모든 경우를 다 작성해줄 수 없기 때문이다. 이때 사용하는 것이 제네릭 타입이다.
제네릭(generics) 타입
:제네릭 타입은 콘크리트 타입 대신 쓸 수 있는 placeholder 역할을 한다.
:사용자가 사용한 타입을 보고 유추하여 대체해주는 역할
제네릭(generics) 타입은 call signature를 작성할 때 들어올 확실한 타입을 모를 때 사용한다.
▷여기서 concrete type 이란 number, boolean, void 같은 타입을 말하는데 이런 콘크리트 타입이 아닌 제네릭 타입을 불러올 수 있다.
'제네릭은 선언 시점이 아니라 생성 시점에 타입을 명시하여 하나의 타입만이 아닌 다양한 타입을 사용할 수 있도록 하는 기법이다.'
제네릭 타입을 사용하려면 call signature가 제네릭을 받는다는 것을 알려주어야 한다.
type SuperPrint = {
<TypePlaceholder>(arr: TypePlaceholder[]): void
}
꺽쇠 + 원하는 제네릭 이름을 이용해서 알려줄 수 있다. 이름은 무엇이 되어도 상관없다! 자주 사용하는 이름은 <T>그리고 원래는 타입을 명시했을 자리에 제네릭 타입의 이름을 써주면 된다.
type SuperPrint = {
<TypePlaceholder>(arr: TypePlaceholder[]): void
}
const superPrint: SuperPrint = (arr) => {
arr.forEach(i => console.log(i))
}
superPrint([1, 2, 3, 4]);
superPrint([true, false, true, true]);
superPrint(["1", "2", "3"]);
superPrint([1, 2, true, false]);
이제 타입을 일일이 정의하지 않아도 타입 스크립트에서 타입을 유추하여 call signature에서 그 타입으로 대체하기 때문에 오류도 나지 않고 편리하다!
이제 superPrint 함수가 배열을 인자로 받고 그 첫 번째 요소를 리턴하도록 해보자.
type SuperPrint = {
<TypePlaceholder>(arr: TypePlaceholder[])=> TypePlaceholder
}
const superPrint: SuperPrint = (arr) => arr[0]
const a = superPrint([1, 2, 3, 4]); //a: number
const b = superPrint([true, false, true, true]); //b: boolean
const c = superPrint(["1", "2", "3"]); //c: string
const d = superPrint([1, 2, true, false]); //d: number | boolean
any와 제네릭의 차이점
그럼 제네릭을 쓰는 대신에 any를 써도 되지 않을까? 생각할 수 있지만 any를 쓰게 되면 더 이상 코드를 보호할 수 없게 된다. 만약 d.toUpperCase()를 실행하는 경우 오류가 날 것이다. 왜냐하면 d는 number일 것이기 때문이다. 하지만 이것을 제네릭이 아닌 any로 하면 d 역시 any가 되어 오류가 날 수 있는 상황에서 보호받지 못한다.
제네릭을 추가하고 싶다면?
type SuperPrint = {
<T, M>(arr: T[], b:M) => T
}
타입 스크립트는 제네릭을 처음 인식했을 때와 제네릭의 순서를 기반으로 제네릭의 타입을 알 게 된다. 따라서 T는 첫 번째 parameter이고.. 이런 것을 따로 설명하지 않아도 (arr: T [], b:M) 이 부분에서 타입 스크립트는 순서에 따라 분석해서 알게 된다.
제네릭을 call signature가 아닌 다른 방법으로 선언하기
function superPrint<T>(a: T){
return a[0]
}
객체에서 제네릭 사용하기
type Player<E> = {
name: string
extraInfo: E
}
const nico: Player<{favFood: string}> = {
name: "nico",
extraInfo: {
favFood: "kimchi"
}
}
코드를 더 확장하고 싶다면,
type Player<E> = {
name: string
extraInfo: E
}
type NicoPlayer = Player<{favFood: string}>
const nico: NicoPlayer = {
name: "nico",
extraInfo: {
favFood: "kimchi"
}
}
'개발 공부 > TypeScript' 카테고리의 다른 글
TypeScript #4 [readonly, 추상화, 인터페이스] (0) | 2022.08.01 |
---|---|
TypeScript #3 [class, 추상 클래스, 추상 메소드, protected] (0) | 2022.07.31 |
TypeScript #1 [type] (0) | 2022.07.25 |
call signatures
:함수 위에 마우스를 올렸을 때 보게 되는 것, 함수가 어떻게 호출되는지 알려준다.
인자의 타입과 함수의 반환 타입을 알려주는 것

call signature 선언하기
type Add = (a: number, b:number) => number; //call signature 선언
const add: Add = (a, b) => a+b
이렇게 콜 시그니처를 선언해두고 사용하면 함수를 선언할 때 인자나 리턴 값이 타입을 말해줄 필요가 없다.
오버 로딩(overloading)
=function overloading
= method 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
}
이렇게 Add라는 한 타입 안에 서로 다른 콜 시그니처를 가지고 있을 땐 b의 타입에 따라 리턴 값이 달라질 수 있도록 b의 값을 먼저 확인해야 한다. 상황에 따라 보내주는 값이 달라지는 것.
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)
}
}
이렇게 인자 타입에 따라 다른 리턴 값을 가지게 되는 경우가 많이 생긴다고 한다!
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
}
이렇게 가지고 있는 파라미터의 개수가 다를 땐 c의 파라미터는 옵션처럼 여겨진다. 따라서 c 파라미터를 함수에서 사용할 땐 타입을 정의해주어야 한다. 따라서 'c는 선택사항인데 이 c 파라미터는 number일지도 모른다.'라는 뜻으로 c의 타입을 정의해주었다.
다형성(polymorphism)
:여러 가지의 구조
배열의 값에 어떤 타입이 들어와도 콘솔에 찍는 함수를 만들어보자
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, true]);
superPrint(["1", "2", "3"]);
이렇게 있는 경우 number타입 배열과 boolean 타입 배열은 정상 작동 하지만 string 설정해주지 않았기 때문에 오류가 난다. 그렇다면 string도 똑같이 추가해주는 것이 좋을까? 그러면 작동은 하지만 좋은 방법은 아니다. 모든 경우를 다 작성해줄 수 없기 때문이다. 이때 사용하는 것이 제네릭 타입이다.
제네릭(generics) 타입
:제네릭 타입은 콘크리트 타입 대신 쓸 수 있는 placeholder 역할을 한다.
:사용자가 사용한 타입을 보고 유추하여 대체해주는 역할
제네릭(generics) 타입은 call signature를 작성할 때 들어올 확실한 타입을 모를 때 사용한다.
▷여기서 concrete type 이란 number, boolean, void 같은 타입을 말하는데 이런 콘크리트 타입이 아닌 제네릭 타입을 불러올 수 있다.
'제네릭은 선언 시점이 아니라 생성 시점에 타입을 명시하여 하나의 타입만이 아닌 다양한 타입을 사용할 수 있도록 하는 기법이다.'
제네릭 타입을 사용하려면 call signature가 제네릭을 받는다는 것을 알려주어야 한다.
type SuperPrint = {
<TypePlaceholder>(arr: TypePlaceholder[]): void
}
꺽쇠 + 원하는 제네릭 이름을 이용해서 알려줄 수 있다. 이름은 무엇이 되어도 상관없다! 자주 사용하는 이름은 <T>그리고 원래는 타입을 명시했을 자리에 제네릭 타입의 이름을 써주면 된다.
type SuperPrint = {
<TypePlaceholder>(arr: TypePlaceholder[]): void
}
const superPrint: SuperPrint = (arr) => {
arr.forEach(i => console.log(i))
}
superPrint([1, 2, 3, 4]);
superPrint([true, false, true, true]);
superPrint(["1", "2", "3"]);
superPrint([1, 2, true, false]);
이제 타입을 일일이 정의하지 않아도 타입 스크립트에서 타입을 유추하여 call signature에서 그 타입으로 대체하기 때문에 오류도 나지 않고 편리하다!
이제 superPrint 함수가 배열을 인자로 받고 그 첫 번째 요소를 리턴하도록 해보자.
type SuperPrint = {
<TypePlaceholder>(arr: TypePlaceholder[])=> TypePlaceholder
}
const superPrint: SuperPrint = (arr) => arr[0]
const a = superPrint([1, 2, 3, 4]); //a: number
const b = superPrint([true, false, true, true]); //b: boolean
const c = superPrint(["1", "2", "3"]); //c: string
const d = superPrint([1, 2, true, false]); //d: number | boolean
any와 제네릭의 차이점
그럼 제네릭을 쓰는 대신에 any를 써도 되지 않을까? 생각할 수 있지만 any를 쓰게 되면 더 이상 코드를 보호할 수 없게 된다. 만약 d.toUpperCase()를 실행하는 경우 오류가 날 것이다. 왜냐하면 d는 number일 것이기 때문이다. 하지만 이것을 제네릭이 아닌 any로 하면 d 역시 any가 되어 오류가 날 수 있는 상황에서 보호받지 못한다.
제네릭을 추가하고 싶다면?
type SuperPrint = {
<T, M>(arr: T[], b:M) => T
}
타입 스크립트는 제네릭을 처음 인식했을 때와 제네릭의 순서를 기반으로 제네릭의 타입을 알 게 된다. 따라서 T는 첫 번째 parameter이고.. 이런 것을 따로 설명하지 않아도 (arr: T [], b:M) 이 부분에서 타입 스크립트는 순서에 따라 분석해서 알게 된다.
제네릭을 call signature가 아닌 다른 방법으로 선언하기
function superPrint<T>(a: T){
return a[0]
}
객체에서 제네릭 사용하기
type Player<E> = {
name: string
extraInfo: E
}
const nico: Player<{favFood: string}> = {
name: "nico",
extraInfo: {
favFood: "kimchi"
}
}
코드를 더 확장하고 싶다면,
type Player<E> = {
name: string
extraInfo: E
}
type NicoPlayer = Player<{favFood: string}>
const nico: NicoPlayer = {
name: "nico",
extraInfo: {
favFood: "kimchi"
}
}
'개발 공부 > TypeScript' 카테고리의 다른 글
TypeScript #4 [readonly, 추상화, 인터페이스] (0) | 2022.08.01 |
---|---|
TypeScript #3 [class, 추상 클래스, 추상 메소드, protected] (0) | 2022.07.31 |
TypeScript #1 [type] (0) | 2022.07.25 |