A Developing Developer

DAY 51. TypeScript 심화 2일차 본문

내일배움캠프 4기/TIL

DAY 51. TypeScript 심화 2일차

H-JJOO 2023. 1. 26. 19:55
  • 문제발생

TypeScript (제네릭 타입, 유틸리티 타입)

  • 시도

-

  • 해결방안

-

  • 알게 된 것

1. 제네릭 타입 (Generic Types) : 선언 시점이 아닌 생성 시점에 타입을 명시하여 하나의 타입만이 아닌 다양한 타입을 사용할 수 있게 해주는 기법! (대표적 식별자 T, 그외 U, V 가 있지만 관용적인 식별자인 T 를 쓴다)

 - 객체 

interface MyInterface<GenericValue> {
  value: GenericValue;
}

const stringObject: MyInterface<string> = { value: "hello, world!" };
const numberObject: MyInterface<number> = { value: 1234 };
const stringArrayObject: MyInterface<Array<string>> = { // MyInterface<string[]> 기능적으로는 동일
  value: ["hello", "world!"],
};

// 재활용, 무슨타입을 명시했는지 보기 쉽다.

- 함수

type User = {
  email: string;
  name: string;
};

// 타입 변수, 캡처를해서 넘겨주고, 반환 타입 T
function getData<T>(data: T): T {
  return data;
}

// 에러 없이 콘솔로그 되는 유효한 호출
console.log(getData<string>("string data"));
console.log(getData<number>(1234));
console.log(getData<User>({ email: "email@email.com", name: "katie" }));
console.log(getData<string[]>(["string", "data"]));
console.log(getData<string[]>([])); // 빈 배열도 유효한 인자입니다!

 - 클래스

interface IStack<T> {
  push(item: T): void;
  pop(): T | undefined;
  peek(): T | undefined;
  size(): number;
}

class Stack<T> implements IStack<T> {
  private storage: T[] = [];

// storage 배열의 길이를 4로 제한
  constructor(private capacity = 4) {}

// storage 배열에 인자로 받는 item 값을 담아주는 함수
  push(item: T): void {
    if (this.size() === this.capacity) {
      throw Error("stack is full");
    }

    this.storage.push(item);
  }

// storage 배열의 제일 마지막에 위치한 값을 뽑아내는 함수
  pop(): T | undefined {
    return this.storage.pop();
  }

// storage 배열의 마지막 index에 위치한 값을 다루는 함수
  peek(): T | undefined {
    return this.storage[this.size() - 1];
  }

// storage 배열의 길이를 반환하는 함수
  size(): number {
    return this.storage.length;
  }
}

const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
numberStack.push(3);
numberStack.push(100);

console.log(numberStack.peek()); // 100
console.log(numberStack.size()); // 4

// 이미 4개의 값이 stroage 배열에 가득차있으므로, 하나의 값을 더 추가하면 에러발생
// numberStack.push(101); // ❌ Error: stack is full

2. 유틸리티 타입 (Utility Types) : 외부 라이브러리에서 불러오는 타입이나 import 해올 수 없는 타입을 조작해서 원하는 타입으로 변환시키는데 유용하다.

 - Partial<Type> : 특정 타입에 속해있는 집합을 모두 선택적으로 만드는 타입으로 변환

interface Toppings {
  tomatoes: boolean;
  onion: boolean;
  lettuce: boolean;
  ketchup: boolean;
}

// 모든 타입을 반드시 명시
const toppingsIWant: Toppings = {
  tomatoes: true,
  onion: true,
  lettuce: true,
  ketchup: true,
};

// 모든 타입을 선택적 명시
const partialToppingsIWant: Partial<Toppings> = {
  tomatoes: true,
  onion: undefined,
};

console.log("wt", partialToppingsIWant);

 

- Required<Type> : Partial 의 반대! 특정 타입에 속해있는 집합을 모두 필수로 변환하는 타입!

// 선택적으로
interface BubbleTeaOrder {
  tea: boolean;
  straw?: boolean;
}

// 필수!
const myBubbleTeaOrder: Required<BubbleTeaOrder> = {
  tea: true,
  straw: true,
};

// 선택적!
const myBubbleTeaOrder2 = {
  tea: true,
};

// 라이브러리를 가져와서 쓰는 경우가 많은데, 해당 라이브러리에서 export 해주는 타입만 사용가능한데, 사용하고자 하는 용도에 맞게 쓰기위해서는 유틸리티 타입을 이용해야한다. (지정된 타입 조작)

 

- Readonly<Type> : 한 타입의 집합을 읽기권한만 가능하게 변환해주는 타입

interface BankAccount {
  accountNumber: string;
  balance: bigint;
}

const myAccount: Readonly<BankAccount> = {
  accountNumber: "1234",
  balance: BigInt(Number.MAX_SAFE_INTEGER),
};

// 컴파일되지 않습니다
//myAccount.accountNumber = "123"; // ❌ Cannot assign to 'accountNumber' because it is a read-only property.
//myAccount.balance = BigInt(Number.MIN_VALUE); // ❌ Cannot assign to 'balance' because it is a read-only property.

console.log(myAccount);

 

- Record<Keys, Type> : 객체 타입을 설립하는데 쓰임

type Type = string[];
type TypeII = Array<string>;

type ObjectTypeRecord = Record<string, string>;
type ObjectTypeObject = { [x: string]: string };

type Country = "Korea" | "USA" | "Canada" | "UK"; // enum으로 구현해도 됩니다
type CountryCode = 82 | 1 | 44; // enum으로 구현해도 됩니다

// syntaxt 객체 문법
type CountryToCountryCode = Record<Country, CountryCode>;

const countries: CountryToCountryCode = {
  Korea: 82,
  USA: 1,
  Canada: 1,
  UK: 44,
};

 

- Omit<Type, keys> : 특정 타입에 구성되어있는 프로퍼티를 생략시킬 때 쓰는 타입!

interface UserInfo {
  userName: string;
  favoriteColor: string;
  email: string;
  password: string;
}

type LessUserInfo = Omit<UserInfo, "password" | "email">;

const newUser: LessUserInfo = {
  userName: "pony",
  favoriteColor: "rainbow",
  // 생략시킨 email이 속해있어서 컴파일되지 않습니다
  //email: "hello@world.hello", // ❌ Object literal may only specify known properties, and 'email' does not exist in type 'LessUserInfo'.
};

// UserInfo 는 조작되지않고 새로운 타입을 만든다.

 

- Exclude<UnionType, ExcludeMembers> : 유니언 타입에 속해있는 속성들을 생략할 때 사용, Omit 은 객체 타입

type MyType = "dog" | "cat" | "alpaca";
type ExcludedType = Exclude<MyType, "cat" | "alpaca">;
type LessMyType = Exclude<MyType, "alpaca">;

const onlyDogAllowed: ExcludedType = "dog"; // ✅
//const noAlpaca: ExcludedType = "alpaca"; // ❌
const catOrDogAllowed: LessMyType = "cat";

//----------------------------------------------------

type OnChange = (isDone: boolean) => boolean;
type GroupOfTypes = string | undefined | OnChange;
type FunctionType = Exclude<GroupOfTypes, string | undefined>; // Onchange 만 남음

const onChangeHandler: FunctionType = (isDone) => isDone; // ✅
console.log(onChangeHandler(true));

//const today: FunctionType = "greate day"; // ❌
// Onchange 타입을 반영해서 오른쪽이 함수여야 함

 

- Pick<Type, Keys> : 한 타입의 특정 프로퍼티들만 뽑아쓸수 있도록 도와주는 타입! Omit 의 반대!

interface User {
  firstName: string;
  lastName: string;
}

interface Student {
  user: User;
  isGraduated: boolean;
  school: string;
}

type StudentName = Pick<Student, "user" | "isGraduated">;
const studentName: StudentName = {
  user: {
    firstName: "winnie",
    lastName: "pooh",
  },
  isGraduated: true,
};

console.log(studentName);

 

- Extract<Type, Union> : 타입에서 필요한 유니언만 뽑아쓴다! Exclude 는 생략! (약간 반대라고 생각하면?)

type MyPet = "dog" | "cat" | "alpaca";
type ExtractedType = Extract<MyPet, "alpaca" | "cat">;

const onlyAlpacaOrCatAllowed: ExtractedType = "cat"; // 또는 "alpaca"만 할당 가능

console.log(onlyAlpacaOrCatAllowed);

 

- NonNullable<Type> : 특정 타입에서 null 또는 undefined 를 생략해주는 타입! 생략보다는 null 과 undefined 를 쓸수 없게하는 거 같은데?

type QueryParam = string | string[] | undefined | null;
type NonNullableQueryParam = NonNullable<QueryParam>;

const queryParam: NonNullableQueryParam = "orders"; // 문자열은 허용되는 타입입니다
// const forbiddenQueryParam: NonNullableQueryParam = undefined; 
// 허용되지 않는다

 

[결론]

https://www.typescriptlang.org/ko/docs/handbook/utility-types.html

 

Documentation - Utility Types

Types which are globally included in TypeScript

www.typescriptlang.org

외에도 공부할거 많다... 

 

개발자... 진짜 관둘때까지 공부해야하는 직업이라할만 하다.

'내일배움캠프 4기 > TIL' 카테고리의 다른 글

DAY 57. SOLID 원칙  (0) 2023.02.06
1.27 ~ 2.3 나태지옥  (0) 2023.02.06
DAY 50. TypeScript 심화 1일차  (0) 2023.01.26
DAY 49. TypeScript 5일  (0) 2023.01.20
DAY 48. TypeScript 4일차  (0) 2023.01.19