가수면

타입 심화 본문

JavaScript/TypeScript

타입 심화

니비앙 2023. 4. 12. 01:44

인터섹션 타입

두 개 이상의 타입을 결합하여 새로운 타입을 만들어내 여러 타입을 모두 만족시키는 타입을 의미한다.

예시)

interface IPerson {
  name: string;
  age: number;
}

interface IEmployee {
  company: string;
  position: string;
}

type IEmployeePerson = IPerson & IEmployee;

keyof

객체의 키 값들을 숫자나 문자열 리터럴 유니언으로 생성

enum의 경우는 keyof typeof로 사용

type Point = { x: number; y: number };
type P = keyof Point; // “x” | “y”


enum LogLevel {
  ERROR, WARN, INFO, DEBUG
}
// 'ERROR' | 'WARN' | 'INFO' | 'DEBUG';
type LogLevelStrings = keyof typeof LogLevel;

추상 클래스

인터페이스와 비슷한 역할.

나머지는 그대로 상속되니 클래스 별로 다르게 지정하고 싶은 속성에 abstract를 부여하면 되는 것 같다.

abstract class Developer {
  abstract coding(): void; // 'abstract'가 붙으면 상속 받은 클래스에서 무조건 구현해야 함
  drink(): void {
    console.log('drink sth');
  }
}

class FrontEndDeveloper extends Developer {
  coding(): void {
    // Developer 클래스를 상속 받은 클래스에서 무조건 정의해야 하는 메서드
    console.log('develop web');
  }
  design(): void {
    console.log('design web');
  }
}
const dev = new Developer(); // error: cannot create an instance of an abstract class
const josh = new FrontEndDeveloper();
josh.coding(); // develop web
josh.drink(); // drink sth
josh.design(); // design web

제네릭

여러 가지 타입을 유연하게 부여하고 싶을 때 사용할 수 있음.

function getText<T>(text: T): T {
  return text;
}

getText<string>('hi');
getText<number>(10);
getText<boolean>(true);
function logText<T, U>(text: T[]): U[] {
  console.log(text.length);
  return text;
}

유연하게 가져가면서도 특정 속성은 정의하고 싶은 경우

interface LengthWise {
  length: number;
}

function logText<T extends LengthWise>(text: T): T {
  console.log(text.length);
  return text;
}

맵드 타입

기존 타입의 프로퍼티를 변환하거나 조작하여 새로운 타입을 생성할 수 있다.

객체의 프로퍼티 타입을 변환하거나 동일한 타입에 대해서 반복해 선언하는 것을 피할 때 유용함

기본 형태

type MappedType = { 
  [P in KeyType]: ValueType 
}

실사용

보통은 keyof와 많이 씀

type Subset<T> = {
  [K in keyof T]?: T[K];
}
type OriginalType = {
  name: string;
  age: number;
};

type ModifiedType = {
  [P in keyof OriginalType]: string;
};
interface UserProfile {
  username: string;
  email: string;
  profilePhotoUrl: string;
}

interface UserProfileUpdate {
  username?: string;
  email?: string;
  profilePhotoUrl?: string;
}

type UserProfileUpdate = {
  [p in keyof UserProfile]?: UserProfile[p]
}

Partial

특정 타입의 부분 집합을 만족하는 타입을 정의할 수 있다.

interface Address {
  email: string;
  address: string;
}

type MayHaveEmail = Partial<Address>;
const me: MayHaveEmail = {}; // 가능
const you: MayHaveEmail = { email: 'test@abc.com' }; // 가능
const all: MayHaveEmail = { email: 'capt@hero.com', address: 'Pangyo' }; // 가능

Pick

특정 타입에서 몇 개의 속성을 선택(pick)하여 타입을 정의할 수 있다.

interface Hero {
  name: string;
  skill: string;
}
const human: Pick<Hero, 'name'> = {
  name: '스킬이 없는 사람',
};
type HasThen<T> = Pick<Promise<T>, 'then' | 'catch'>;
let hasThen: HasThen<number> = Promise.resolve(4);
hasThen.th // 위에서 'then'만 선택하면 'then'만 제공, 'catch' 선택하면 'catch만 제공'

Omit

특정 타입에서 지정된 속성만 제거한 타입을 정의할 수 있다.

interface AddressBook {
  name: string;
  phone: number;
  address: string;
  company: string;
}
const phoneBook: Omit<AddressBook, 'address'> = {
  name: '재택근무',
  phone: 12342223333,
  company: '내 방'
}
const chingtao: Omit<AddressBook, 'address'|'company'> = {
  name: '중국집',
  phone: 44455557777
}

 

기타 - 타입스크립트 적용 시 팁

타입 지정하지 않은 속성에 대해 추가하거나 검증할 때

마이그레이션 등의 상황 시 images가 undefined거나 지정되지 않은 상황에서 아래 처럼 해당 속성에 대해 검증하거나 추가 할당하려고 할 때 타입 에러가 발생한다.

if (item.images) {
lastImageIndex = index;
}

이 경우 해당 속성이 있는지 'images' in item와 같은 방식으로 검증할 수 있다.

 

if ('images' in item) {
    lastImageIndex = index;
}

둘의 차이는 item.images가 실제 값이 할당되어 있는지(undefined, "" 등 false 반환)를 확인하고, 'images' in item는 해당 속성이 있는지만(값이 undefined, "" 등이어도 해당 속성이 있다면 true 반환) 확인한다.

'JavaScript > TypeScript' 카테고리의 다른 글

event 타입  (0) 2023.05.08
ref를 props로 넘겨줄 때  (0) 2023.04.20
넘어오는 데이터가 두 타입 중 하나일 때  (0) 2023.04.05
타입스크립트에서 children  (0) 2023.04.01
라이브러리 매개변수  (0) 2023.03.27
Comments