노마드코더 Typescript로 블록체인 만들기 강의 정리 - 2 (챕터3)👊 내 것으로 만들자
type Add = (a:number, b:number) => number; //함수의 call signature타입 지정
const add = (a:number, b:number) => a+b //call signature 사용 전
const add:Add = (a,b) => a+b //call signature 사용 후
참고✔️
예제1과 2는 같은 call signature이다.//예제 1 type Add = (a: number, b: number) => number //예제 2 type Add = { (a: number, b: number) => number }
오버로딩의 예시
자주 볼 만한 예시는 아님.
type Add = {
(a: number, b: number) : number
(a: number, b: string) : number
}
//오류⛔
const add: Add = (a,b) => a+b
//작동✔️
const add: Add = (a,b) => {
if(typeof b === 'string') return a
return a+b
}
자주 볼 법한 오버로딩의 예시
type Config = {
path: string,
state: object
}
type Push = {
(path: string): void //void: 아무것도 리턴하지 않는 것
(config: Config): void
}
const push: Push = (config) => { //confi
//타입이 string일 때
if(typeof config === "string") {console.log(config)}
//타입이 Config일 때
else {
console.log(config.path) //또는 config.state도 가능
}
}
type Add = {
(a: number, b: number): number;
(a: number, b: number, c: number): number; //c는 옵션
};
//오류⛔
const add: Add = (a, b, c) => { //c가 옵션이라는 것과 c의 타입을 알려줘야함.
if (c) return a + b + c;
return a + b;
};
//작동✔️
type Add = {
(a: number, b: number): number;
(a: number, b: number, c: number): number; //c는 옵션
};
const add: Add = (a, b, c?: number) => {
if (c) return a + b + c;
return a + b;
};
//작동✔️ 위의 예제 같은 기능이다.
type Add2 = {
(a: number, b: number, c?: number): number; //c는 옵션
};
const add2: Add2 = (a, b, c) => {
if (c) return a + b + c;
return a + b;
};
아직 오버로딩이 어디에 어떻게 쓰일지는 예상이 안가지만 이해완료!
poly
: many, several, much, multi... morphos/porphic
: form, structure...
➡️Polymorphism
: 여러가지 구조(형태)
3-1강의 오버로딩도 어떻게보면 polyporphism의 한 형태이다.
generic
을 사용해서 함수의 다형성을 표현한다.
generic
을 사용하는 경우: 함수의 call signature를 작성할 때, 어떤 타입이 들어올지 확실하게 모르는 경우주의사항⚠️
generic은 대문자로 시작해야한다.
superPrint: 배열을 인자로 받고, 그 배열의 요소들을 콘솔에 찍는 함수
배열의 요소로 어떤 타입이든 들어갈 수 있다.const superPrint: SuperPrint = (arr) => { arr.forEach(i => console.log(i)); }
generic을 사용하지 않은 type
type SuperPrint = {
(arr: number[]): void;
(arr: boolean[]): void;
...
...
...
//또는 모든 가능성을 고려해 타입을 예상해야 한다.
(arr: (number|boolean ...)[]): void;
}
generic을 사용한 type
type SuperPrint = {
<TypePlaceHolder>(arr: TypePlaceHolder[]): void;
//<>안의 이름은 마음대로 지어도 됨. 주로 <T>라고 사용함
}
superPrint2: 배열을 인자로 받고, 그 배열의 첫번째 요소를 return하는 함수
배열의 요소로 어떤 타입이든 들어갈 수 있다.const superPrint2: SuperPrint2 = (arr) => arr[0];
generic을 사용한 type
type SuperPrint2 = {
<TypePlaceHolder>(arr: TypePlaceHolder[]): TypePlaceHolder;
//리턴값의 타입을 void-> TypePlaceHolder로 바꿔주었다.
}
generic(placeholder)
이 any
와 다른 점은 우리가 요구하는 대로 call signature가 생성된다는 것이다. any
를 사용하면 함수의 call signature가 자동으로 생성되지 않기 때문에 발생하는 오류를 인식할 수 없다.아래 예시를
type SuperPrint = (a:any[]) => any
로 바꾼다면
a.toUpperCase()
는 정상작동한다. 즉, 오류를 미리 잡을 수 없다.
type SuperPrint = <T>(arr:T[]) => T;
const superPrint: SuperPrint = (arr) => {
return arr[0];
}
const a = superPrint([1,2,3]);
a.toUpperCase(); //오류⛔
//generic을 사용하면 a의 타입이 number이기 때문에 미리 오류를 잡아주지만
//any를 사용하면 a의 타입이 any이기 때문에 오류를 잡지 못한다.
type SuperPrint = <T,M>(arr:T[], b:M) => T;
const superPrint: SuperPrint = (arr) => arr[0];
//함수의 리턴값에 따라 SuperPrint의 리턴 generic도 달라질 수 있다.
제네릭을 함수에 사용하는 방법 1
- 함수의 Alias타입에 적용
type SuperPrint = <T>(a: T[]) => T; const superPrint:SuperPrint = (a) => a[0];
제네릭을 함수에 사용하는 방법 2
- 함수에 직접 적용
function superPrint<T>(a: T[]){ return a[0]; } //또는 const superPrint = <T>(a: T[]) => a[0];
//예시 - 꼭 이렇게까지 확장하지 않아도 되지만 예시!
type Player<E> = {
name: string;
extraInfo: E;
};
type NicoExtra = {
favFood: string;
};
//NicoPlayer타입안에 NicoExtra타입을 넣음
type NicoPlayer = Player<NicoExtra>;
const nico: NicoPlayer = {
name: 'nico',
extraInfo: {
favFood: 'kimchi',
},
};
//위아래가 똑같은 타입이다
type A = number[];
type A = Array<number>;
타입스크립트가 점점 재밌다. 3챕터부터는 이해가 잘 안돼서 2번씩 본 부분도 있지만...
이제부터는 토이프로젝트로 직접 만들면서 들어보는걸로!