본문 바로가기
Notes/TypeScript

TypeScript Chapter. 4

by seyoonagain 2024. 12. 8.

TypeScript


DOM 요소의 타입

타입스크립트 자체적으로 DOM API의 타입을 제공한다.

// HTMLElement | null
const link_1 = document.getElementById('myLink');

// Element | null
const link_2 = document.querySelector('#myLink');

// HTMLButtonElement
const btn = document.getElementById('btn') as HTMLButtonElement;

// HTMLParagraphElement
const p = document.createElement('p');

타입 좁히기 Type Narrowing

변수의 가능한 타입을 구체적으로 좁혀나가는 과정을 말한다.
여러 타입의 매개변수에 대해 타입 별로 적절한 처리를 해주기 위해 사용되며,
typeof, instanceof, === 등을 통해 타입의 범위를 좁힐 수 있다.

type Id = string | number;

const id: Id = 1;

function getId(id: Id) {
  if (typeof id === 'number') {
    return id;
  }

  return Number(id);
}
type iOS = { iMessage: () => void };
type android = { message: () => void };

function sendMessage(os: iOS | android) {
  if ('iMessage' in os) {
    os.iMessage();
  } else {
    os.message();
  }
}
class ApiResponse {
  data: any;
}
class ErrorResponse {
  message: string;
}

const apiResponse = new ApiResponse();
const errorResponse = new ErrorResponse();

function isErrorResponse(
  response: ApiResponse | ErrorResponse
): response is ErrorResponse {
  return (response as ErrorResponse).message !== undefined;
}

const response = { message: 'error...' };

if (isErrorResponse(response)) {
  console.log(response.message);
}
type SuccessResponse {
  type:'success',
  data: any
}

type ErrorResponse = {
  type: 'error',
  message: string
}

type ApiResponse = SuccessResponse | ErrorResponse

function handleResponse (response: ApiResponse) {
  if(response.type==='success') {
    console.log('data:', response.data)
  } else{
    console.log(response.message)
  }
}

고급 타입

교차 타입

여러 타입을 조합하여 사용함으로써, 타입의 재사용성을 높일 수 있다.

type A = { name: string };
type B = { age: number };

type Person = A & B;

const person: Person = {
  name: 'John',
  age: 33,
};

조건부 타입

type IsNumber<T> = T extends number ? 'Yes' : 'No';

type Result_1 = IsNumber<number>; // 'Yes'
type Result_2 = IsNumber<string>; // 'No'

type JsonOrText<T extends 'json' | 'text'> = T extends 'json' ? object : string;

type JsonResponse = JsonOrText<'json'>; // object
type TextResponse = JsonOrText<'text'>; // string
type MyObject = {
  a: number;
  b: string;
  c: boolean;
};

// MyObject의 key를 literal union 타입으로 추출
type Keys = keyof MyObject; // 'a' | 'b' | 'c'

매핑 타입

type OptionalType<T> = {
  [P in keyof T]?: T[P];
};

type ReadonlyType<T> = {
  readonly [P in keyof T]: T[P];
};

type UserType = {
  id: number;
  name: string;
  age: number;
  email: string;
};

// UserType의 key를 하나씩 돌면서 optional 속성으로 바꾸기
type OptionalUserType = OptionalType<UserType>;

// UserType의 key를 하나씩 돌면서 readonly 속성으로 바꾸기
type ReadonlyUserType = ReadonlyType<UserType>;

유틸리티 타입

유틸리티 타입 설명
Partial<T> 타입 T의 모든 속성을 optional로 변환
Readonly<T> 타입 T의 모든 속성을 readonly로 변환
Pick<T,K> 타입 T에서 속성 K만 골라 타입 생성
Omit<T,K> 타입 T에서 속성 K를 제외하여 타입 생성
Record<K,T> K 타입의 key와 T 타입의 값으로 구성된 타입 생성
Parameters<T> 함수 타입 T의 매개변수 타입들을 튜플 타입으로 추출

Partial<T>

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

type PartialUser = Partial<User>;

const user: PartialUser = {
  id: 1,
  name: 'Meghan',
};

Readonly<T>

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

const user: Readonly<User> = {
  id: 1,
  name: 'Meghan',
  email: 'meghan@email.com',
};

user.id = 10; // error

Pick<T, K>

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

type UserWithNameOnly = Pick<User, 'name'>;

const userWithName: UserWithNameOnly = {
  name: 'Lee',
};

Omit<T, K>

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

type UserWithoutEmail = Omit<User, 'email'>;
type UserWithOnlyEmail = Omit<User, 'id' | 'name'>;

const userWithoutEmail: UserWithoutEmail = {
  id: 2,
  name: 'Meghan',
};

const userWithOnlyEmail: UserWithOnlyEmail = {
  email: 'meghan@email.com',
};

Record<K, T>

type Country = 'South Korea' | 'United States' | 'Canada';
type Capital = string;

type CountryCapitals = Record<Country, Capital>;

const capitals: CountryCapitals = {
  'South Korea': 'Seoul',
  'United States': 'Washington D.C.',
  'Canada': 'Ottawa',
};

type CountryInfo = {
  capital: string;
  population: number;
  continent: string
}

type CountryInfoMap = Record<Country, CountryInfo>

cons countryInfo: CountryInfoMap = {
  'South Korea': {
    capital: 'Seoul',
    population: 51_000_000,
    continent: 'Asia'
  },
  'United States': {
    capital: 'Washington D.C.',
    population: 331_000_000,
    continent: 'North America'
  },
  'Canada': {
    capital: 'Ottawa',
    population: 83_000_000,
    continent: 'North America'
  },
}

Parameters<T>

type User = (id: number, name: string) => void;
type Params = Parameters<User>; // [id: name, name: string]

function saveUser(...params: Params) {
  const [id, name] = params;
}

saveUser(1, 'Meghan');

상수 단언

const someObject = {} as const

배열 또는 객체를 리터럴 타입이자 변경 불가능한 상태로 만듦으로써,
컴파일러의 정확한 타입 추론을 돕고, 코드 범위 내에서 불변성을 유지하도록 돕는다.

const book = {
  title: 'book title',
  author: 'book author',
} as const;

// book.title = "book title changed";
// error: Cannot assign to 'title' because it is a read-only property.

const numbers = [1, 2, 3, 4, 5] as const;

// numbers.push(6);
// error: Property 'push' does not exist on type 'readonly [1, 2, 3, 4, 5]'
const statusCodeMap = {
  101: 'ordered',
  102: 'pending',
  103: 'completed',
} as const;

type statusCodeKeys = keyof typeof statusCodeMap; // 101 \ 102 | 103

function handleStatus(statusCode: statusCodeKeys) {
  const message = statusCodeMap[statusCode]; // 'ordered' | 'pending' | 'completed'
}

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

TypeScript Chapter. 5  (1) 2024.12.09
TypeScript Chapter. 3  (0) 2024.12.07
TypeScript Chapter. 2  (1) 2024.12.07
TypeScript Chapter. 1  (1) 2024.12.05