[Typescript] 타스형! 스터디 4주차

woo·2022년 11월 11일
0

Typescript

목록 보기
4/13
post-thumbnail

➕ zerocho님 typescript all-in-one 강의를 듣고 정리한 내용입니다.

✔ 섹션2

filter 타입 직접 만들기

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] 
  1. Arr에서 타입 T = number 이니까 value 타입도 number
  2. b 는 number[] 이니까 리턴값 타입 T[]
  3. value % 2 === 0타입 boolean
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
      	}
    }
})();
profile
🌱 매일 성장하는 개발자

0개의 댓글