➕ zerocho님 typescript all-in-one 강의를 듣고 정리한 내용입니다.
inferface Arr<T>{
filter(callback: (value : T) => boolean) : T[];
}
const a : Arr<number> = [1,2,3];
const b = a.filter((value) => value % 2 === 0) ;
console.log(b); // [2]
inferface Arr<T>{
filter(callback: (value : T) => boolean) : T[];
}
const c : Arr<number | string> = [1,'2',3,'4',5];
const d = c.filter((value) => typeof value === 'string');
console.log(d); // ['2', '4'] string[]
이렇게 하면 d 타입이 string[]
아니라 (number | string)[]
로 뜨게 됨
따라서 타입을 다시 선언
T말고 다른 타입, 새로운 타입 S를 선언 ( 다른 타입이면 다른 문자를 써야함 !)
이러면 타입 T = number | string
타입 S = string
S는 T의 타입을 좁혀주는 것 (S는 T의 부분집합)
inferface Arr<T>{
filter<S extends T>(callback: (value : T) => value is S) : S[];
}
const c : Arr<number | string> = [1,'2',3,'4',5];
const d = c.filter((value) : value is string => typeof value === 'string');
console.log(d); // ['2', '4'] string[]
v is S
는 커스텀 타입가드( 형식 조건자 ) 이기에 1️⃣번처럼 쓰면 안되고 2️⃣,3️⃣처럼 써야함
// 1️⃣
const d = c.filter((value) => typeof value === 'string'); // ❌
// 2️⃣
const d = c.filter((value) : value is string => typeof value === 'string'); // ⭕
// 3️⃣
const predicate = (value : number | string) : value is string => typeof value === 'string');
const d = c.filter(predicate) // ⭕
타입만들기는 많이 연습해야함~
함수간에 서로 대입할 수 있냐 없냐
용어는 외우지 말자!
function a(x: string): number {
return 0;
}
type B = (x: string) => number | string;
let b: B = a; // B에다가 a를 넣을 수(대입할 수) 있을까? 가능
// 리턴값은 더 넓은 타입에다가 대입할 수 있음
// 여기서는 number | string (더 넓은)에다가 number을 넣은 것!
function a(x: string): number | string {
return 0;
}
type B = (x: string) => number;
let b: B = a; // B에다가 a를 넣을 수(대입할 수) 있을까? 불가능
// number(더 좁은)에다가 number | string는 안됨
// a는 (x:string) => string 또는 (x:string) => number 가능하지만
// b는 (x:string) => number만 가능
function a(x: string | number): number {
return 0;
}
type B = (x: string) => number; // 매개변수는 좁은타입으로 대입할 수 있을까? 가능
let b: B = a;
// a는 x가 string | number 일때 number로
// b는 (x:string) => number만 가능
function a(x: string): number {
return 0;
}
type B = (x: string | number) => number; // 매개변수는 넓은타입으로 대입할 수 있을까? 불가능
let b: B = a;
리턴값과 매개변수 대입 방향은 반대
리턴값은 넓은 타입으로 대입
매개변수는 좁은 타입으로 대입
그래서 이게 가능함🔽
function a(x: string | number): number {
return 0;
}
type B = (x: string) => string | number;
let b: B = a;
let a = 5;
에서 a는 5
인데 ts가 a 타입을 다른 숫자도 될 수 있는 number
로 만드는 것오버로딩 : 같은 타입을 여러번 선언하는 것
타입하나로 모든 것을 다 표현하면 좋지만, 그러지 못할때는 각 케이스마다 다른 방식으로 타입을 지정해주어야함
오버로딩 중 ts가 알아서 맞는 타입으로 매칭시켜줌
// declare : 선언만 하고, body 부분은 구현 안해도됨
declare function add(x: number, y: number): number // add(1,2)
declare function add(x: string, y: string): string // add('1','2')
// interface 안에서도 오버로딩 가능
interface Add {
(x: number, y: number): number;
(x: string, y: string): string;
}
const add: Add = (x: any, y: any) => x + y; //오버로딩 해놓고 실제 구현 부분에서 any 사용해도 됨 (any로 생각하는게 아니라 위에 오버로딩 선언된 타입으로 ts가 생각함)
// class 안에서도 오버로딩 가능
class A{
add(x: number, y: number): number;
add(x: string, y: string): string;
add(x: any, y: any) { // body 부분
return x + y;
}
}
const c = new A().add(1,2);
const d = new A().add('1','2');
ts 에러 처리법
interface Axios{
get(): void;
}
interface CustomError extends Error{
// Error 에는 원래 name, message, stack? 세가지 속성이 있음
response? : { // ? : optional
data : any
}
}
declare const axios: Axios;
(async () => {
try{
await axios.get();
}catch ( error : unknown){
console.error((error as CustomError).response?.data) // 여기서 error 타입 지정해줬지만 일회성!
error.response?.data // ❌ 위에서 일회성으로 타입 지정한거라 타입 모름
(error as CustomError).response?.data // ⭕
}
})();
매번 타입 지정해주면 같은 속성이 반복되니까 변수에 저장해서 사용하면 깔끔함
(async () => {
try{
await axios.get();
}catch ( error : unknown){
const customError = error as CustomError // 변수에 지정해서 사용
console.error(customError.response?.data) /
customError.response?.data
}
})();
as도 자주쓰면 좋지 않다. as는 타입이 unknown일 때만 사용하기!
라이브러리에서 type이 unkonwn 이면 as로 타입을 지정해서 사용해야함! 아니면 타입가드(ex. instanceof)타입을 좁히거나!
따라서 catch문에서 error는 unknown라서 우리가 as로 타입을 지정하는 것!
as가 왜 좋지 않냐면, as는 사람이 직접 타입을 붙이는 것이기 때문에 실수할 가능성이 있다.
위에 코드를 더 정교하게 바꿔보자면,
error가 CutstomError가 아니라 다른 에러일 수 있으니 if(error instanceof CutstomError)
로 타입 걸러주기
추가로 interface는 js 코드로 변환하면 삭제되니까 interface 키워드를 쓰지 못함.
js에서 남아있게 하기 위해서는 class
사용
interface를 타입가드로는 쓰지 못함
interface Axios{
get(): void;
}
class CustomError extends Error{
// Error 에는 원래 name, message, stack? 세가지 속성이 있음
response? : { // ? : optional
data : any
}
}
declare const axios: Axios;
(async () => {
try{
await axios.get();
}catch ( error : unknown){
if(error instanceof CutstomError){ // 타입가드
console.error(error.response?.data)
error.response?.data
}
}
})();