TS study: νƒ€μž… μ‹œμŠ€ν…œ (2)

@choi2021 Β· January 10, 2023 Β· 10 min read

🎚 νƒ€μž… μ‹œμŠ€ν…œ(2)

νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ νƒ€μž… μ‹œμŠ€ν…œ 쀑 객체 래퍼 νƒ€μž…, μž‰μ—¬ 속성 체크, ν•¨μˆ˜ ν‘œν˜„μ‹, νƒ€μž…κ³Ό μΈν„°νŽ˜μ΄μŠ€μ˜ 차이에 λŒ€ν•΄μ„œ μ •λ¦¬ν•΄λ³΄μž ν•œλ‹€.

πŸ“¦ 객체 래퍼 νƒ€μž…

λ¬Έμžμ—΄μ„ μž…λ ₯ν•˜κ³  .을 찍으면 객체처럼 μš°λ¦¬λŠ” λ‹€μ–‘ν•œ λ©”μ†Œλ“œλ₯Ό μ΄μš©ν•  수 μžˆλ‹€. μ΄λ ‡κ²Œ κ°€λŠ₯ν•œ 것은 μžλ°”μŠ€ν¬λ¦½νŠΈκ°€ .을 μ°λŠ” μˆœκ°„ stringμ—μ„œ String 객체 래퍼둜 νƒ€μž…λ³€ν™˜μ΄ 이루어진닀. 객체둜 λ³€ν™˜ν•΄ λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•œ ν›„μ—λŠ” 객체 νƒ€μž…μ—μ„œ λ‹€μ‹œ μ›μ‹œν˜•μœΌλ‘œ λŒμ•„κ°„λ‹€.

μ΄λ ‡κ²Œ μœ μš©ν•œ 객체 래퍼 νƒ€μž…μ΄μ§€λ§Œ μ •μ˜λœ νƒ€μž…μ„ 보면 μ˜€νƒ€κ°€ λ‚˜κΈ° μ‰½κ²Œ λ˜μ–΄μžˆλ‹€.

  • μ›μ‹œν˜•: string, 객체래퍼:String
  • μ›μ‹œν˜•: number, 객체래퍼:Number
  • μ›μ‹œν˜•: boolean, 객체래퍼:Boolean
function getStringLen(foo: String) {
  return foo.length
}

getStringLen("hello")
getStringLen(new String("hello"))

function isGreeting(phrases: String) {
  return ["hello", "good day"].includes(phrases) // Argument of type 'String' is not assignable to parameter of type 'string'.
}

크게 λ¬Έμ œκ°€ λ˜μ§€ μ•Šμ„ 것 κ°™μ•„ λ³΄μ΄μ§€λ§Œ μ›μ‹œν˜•μ€ κ°μ²΄λž˜νΌμ— ν• λ‹Ήν•  수 μžˆλŠ” 반면 객체 λž˜νΌλŠ” μ›μ‹œν˜•μ— ν• λ‹Ήν•  수 μ—†κΈ° λ•Œλ¬Έμ— μ˜€νƒ€λ‘œ μΈν•œ μ—λŸ¬κ°€ λ°œμƒν•œ 것을 λ³Ό 수 μžˆλ‹€. 직접 객체 래퍼λ₯Ό ν• λ‹Ήν•˜μ§€ μ•Šκ²Œ μ£Όμ˜ν•΄μ•Ό 함을 λ³΄μ—¬μ£ΌλŠ” μ˜ˆμ œλ‹€.

βœ’ μž‰μ—¬ 속성 체크

μž‰μ—¬μ†μ„± μ²΄ν¬λŠ” μš°λ¦¬κ°€ μ •μ˜ν•΄ μ€€ νƒ€μž…μ˜ 속성듀 μ™Έμ˜ μΆ”κ°€λœ 속성이 μžˆλŠ”μ§€ ν™•μΈν•˜λŠ” 과정을 μ˜λ―Έν•œλ‹€.

interface Dog {
  age: number
  name: string
}

const dog1: Dog = {
  age: 1,
  name: "바둑이",
  bark() {
    //  'bark' does not exist in type 'Dog'.
    console.log("μ§–κΈ°")
  },
}

μœ„ μ˜ˆμ œμ—μ„œ Dogνƒ€μž…μ— bark 속성이 μ—†κΈ° λ•Œλ¬Έμ— μ—λŸ¬κ°€ λ°œμƒν–ˆμ§€λ§Œ μ•žμ„œ λ°°μ› λ˜ ꡬ쑰적 νƒ€μ΄ν•‘μ˜ 의미둜 λ³΄μ•˜μ„ λ•ŒλŠ” ν•„μš”ν•œ 속성인 age와 name이 있기 λ•Œλ¬Έμ— μ—λŸ¬κ°€ λ‚˜μ§€ μ•Šμ•„μ•Ό ν•  것 κ°™λ‹€.

const dog2 = {
  age: 1,
  name: "바둑이",
  bark() {
    console.log("μ§–κΈ°")
  },
}
const r: Dog = dog2

같은 μ‘°κ±΄μ΄μ§€λ§Œ μ•žμ„  μ˜ˆμ œμ™€λŠ” λ‹€λ₯΄κ²Œ μ—λŸ¬κ°€ λ°œμƒν•˜μ§€ μ•Šμ•˜λ‹€.

interface Options {
  title: string
  darkMode?: boolean
}

function createWindow(options: Options) {
  if (options.darkMode) {
    // setDarkMode()
  }
}
createWindow({
  title: "Spider",
  darkmode: true,
}) // 'darkmode' does not exist in type 'Options'. Did you mean to write 'darkMode'?

const o1: Options = document

λ‘˜μ˜ 차이점은 뭘까?

μ•žμ„  μ—λŸ¬κ°€ λ°œμƒν•œ μ˜ˆμ œλ“€μ€ μž‰μ—¬μ†μ„± 체크가 μ§„ν–‰λ˜μ—ˆκΈ° λ•Œλ¬Έμ— μ—λŸ¬κ°€ λ°œμƒν–ˆλ‹€. μž‰μ—¬μ†μ„± 체크가 λ°œμƒν•˜λŠ” 쑰건은 dog1처럼 객체 λ¦¬ν„°λŸ΄μ„ ν• λ‹Ήν•˜κ±°λ‚˜ createWindow처럼 ν•¨μˆ˜μ— λ§€κ°œλ³€μˆ˜λ₯Ό 전달할 λ•Œ μ μš©λœλ‹€.

μ—λŸ¬κ°€ λ°œμƒν•˜μ§€ μ•Šμ€ κ²½μš°λŠ” λ³€μˆ˜λ₯Ό 톡해 값을 μ „λ‹¬ν–ˆμ„ λ•Œλ‘œ μž„μ‹œ λ³€μˆ˜λ₯Ό μ „λ‹¬ν•˜λ©΄ μž‰μ—¬ 속성 체크가 이루어지지 μ•ŠλŠ”λ‹€. μš°λ¦¬κ°€ μ •μ˜ν•œ μ†μ„±λ§Œ μΆ”κ°€λ˜κ²Œ ν•˜λŠ” κ²½μš°μ— μž‰μ—¬μ†μ„± 체크λ₯Ό μ μš©ν•΄ 였λ₯˜λ₯Ό 찾을 수 μžˆλŠ” μž₯점이 μžˆλ‹€. ν•˜μ§€λ§Œ ꡬ쑰적 νƒ€μ΄ν•‘μ˜ 관점과 μΆ©λŒν•˜κΈ° λ•Œλ¬Έμ— ν•„μš”ν•  λ•Œμ— μ μ ˆν•˜κ²Œ μ‚¬μš©ν•  ν•„μš”κ°€ μžˆμ–΄ λ³΄μ˜€λ‹€.

🎈 ν•¨μˆ˜ ν‘œν˜„μ‹

μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œ ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λŠ” λ°©λ²•μ—λŠ” ν•¨μˆ˜ μ„ μ–Έλ¬Έκ³Ό ν•¨μˆ˜ ν‘œν˜„μ‹μ΄ μžˆλ‹€. νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” ν•¨μˆ˜ ν‘œν˜„μ‹μΌ λ•Œ λ§€κ°œλ³€μˆ˜μ™€ λ°˜ν™˜ 값을 νƒ€μž…μœΌλ‘œ μ„ μ–Έν•  수 μžˆλŠ” μž₯점을 κ°–κ³  μžˆλ‹€.

function add(a: number, b: number) {
  return a + b
}
function sub(a: number, b: number) {
  return a - b
}
function mul(a: number, b: number) {
  return a * b
}
function div(a: number, b: number) {
  return a / b
}

type BinaryFn = (a: number, b: number) => number

const add: BinaryFn = (a, b) => a + b
const sub: BinaryFn = (a, b) => a - b
const mul: BinaryFn = (a, b) => a * b
const div: BinaryFn = (a, b) => a / b

μ•žμ„  μ˜ˆμ œλŠ” ν•¨μˆ˜ 선언문을 μ΄μš©ν•΄ λ§€κ°œλ³€μˆ˜μ˜ νƒ€μž…μ„ μ •ν•΄ μ€€ 경우이고, μ•„λž˜λŠ” ν•¨μˆ˜ ν‘œν˜„μ‹μ— BinaryFnνƒ€μž…μ„ μ΄μš©ν•΄ λ§€κ°œλ³€μˆ˜μ™€ λ°˜ν™˜ κ°’μ˜ νƒ€μž…μ„ ν•œλ²ˆμ— μ •μ˜ν•œ κ²½μš°μ΄λ‹€. ν•¨μˆ˜ μ„ μ–Έλ¬Έμ˜ 경우 일일이 λ§€κ°œλ³€μˆ˜μ˜ νƒ€μž…μ„ μ •ν•΄ μ£Όμ–΄μ•Ό ν•˜κ³ , μ •ν•œ νƒ€μž…μ„ 재 μ‚¬μš©ν•  수 μ—†λŠ” 반면, ν•¨μˆ˜ ν‘œν˜„μ‹μ˜ 경우 ν•¨μˆ˜μ— ν•„μš”ν•œ λ§€κ°œλ³€μˆ˜μ™€ λ°˜ν™˜ κ°’μ˜ νƒ€μž…μ„ ν•œλ²ˆμ— ν‘œν˜„ν•΄ 훨씬 κ°„κ²°ν•˜λ©΄μ„œλ„ 재 μ‚¬μš©μ„±λ„ λ†’μ΄λŠ” 것을 λ³Ό 수 μžˆλ‹€.

async function checkedFetch(input: RequestInfo, init?: RequestInit) {
  const response = await fetch(input, init)
  if (!response.ok) {
    throw new Error(`${response.status}`)
  }
  return response
}

const checkedFetch: typeof fetch = async (input, init) => {
  const response = await fetch(input, init)
  if (!response.ok) {
    throw new Error(`${response.status}`)
  }
  return response
}

μœ„μ˜ μ˜ˆμ œλŠ” λ‚΄κ°€ 주둜 쓰듯이 각각의 λ§€κ°œλ³€μˆ˜μ˜ νƒ€μž…μ„ μ •ν•΄ μ€€ λͺ¨μŠ΅μ΄κ³ , μ•„λž˜λŠ” λ‚΄μž₯된 fetch νƒ€μž…μœΌλ‘œ 훨씬 κ°„κ²°ν•˜κ²Œ λ‚˜νƒ€λ‚Έ λͺ¨μŠ΅μ΄λ‹€. μ΄λ ‡κ²Œ λ™μΌν•œ λ§€κ°œλ³€μˆ˜μ™€ λ°˜ν™˜ κ°’μ˜ νƒ€μž…μ„ κ°€μ§€λŠ” ν•¨μˆ˜μ˜ 경우 ν•¨μˆ˜ μ „μ²΄μ˜ νƒ€μž…μ„ μ •ν•΄ 재 μ‚¬μš©ν•˜λŠ” 것이 효율적인 것을 μƒˆλ‘­κ²Œ μ•Œκ²Œ λ˜μ—ˆλ‹€.

πŸ₯Š νƒ€μž… VS Interface

νƒ€μž…κ³Ό interfaceλŠ” 항상 κ³ λ―Όλ˜λŠ” λ¬Έμ œλ‹€. 곡톡점이 많기 λ•Œλ¬Έμ— μ–΄λ–€ 점을 κΈ°μ€€μœΌλ‘œ μ‚¬μš©ν•΄μ•Ό ν• κΉŒ κ³ λ―Όλ˜λŠ” κ²½μš°κ°€ λ§Žμ•˜λ‹€. μ΄λŸ¬ν•œ 고민은 λ‘˜ λ‹€ κ°€λŠ₯ν•œ κ³΅ν†΅μ μ—μ„œ μ‹œμž‘λ˜μ—ˆλ‹€.

곡톡점

1) νƒ€μž… μ •μ˜

type TState = {
  name: String
  capital: string
}

interface IState {
  name: string
  capital: string
}

λ‘˜ λ‹€ λ™μΌν•˜κ²Œ μ»€μŠ€ν…€ νƒ€μž…μ„ μ •μ˜ν•  수 μžˆλ‹€.

2) Index와 ν•¨μˆ˜ μ •μ˜

type TDict = { [key: string]: string }
interface IDict {
  [key: string]: string
}

type TFn = (x: number) => string
interface IFn {
  (x: number): string
}

λ‘˜ λ‹€ λ™μΌν•˜κ²Œ index와 ν•¨μˆ˜λ₯Ό μ •μ˜ν•  수 μžˆλ‹€.

3) Genericκ³Ό ν™•μž₯

type TPair<T> = {
  first: T
  second: T
}

interface IPair<T> {
  first: T
  second: T
}

interface IStateWithPop extends TState {
  population: number
}

type TStateWithPop = IState & { population: number }

λ‘˜ λ‹€ Generic을 μ‚¬μš©ν•  수 있고 ν™•μž₯도 κ°€λŠ₯ν•˜λ‹€.

4) 클래슀 κ΅¬ν˜„

class StateT implements TState {
  name: string = ""
  capital: string = ""
}

class StateI implements IState {
  name: string = ""
  capital: string = ""
}

클래슀λ₯Ό κ΅¬ν˜„ν•˜λŠ” 것도 λ‘˜ λ‹€ κ°€λŠ₯ν•˜λ‹€.

차이점

λŒ€λΆ€λΆ„μ΄ λ‘˜ λ‹€ κ°€λŠ₯ν•˜κΈ° λ•Œλ¬Έμ— 차이점이 μ—†μ–΄λ³΄μ΄μ§€λ§Œ interface만 κ°€λŠ₯ν•œ 것과 type만이 κ°€λŠ₯ν•œ 역할이 μžˆλ‹€.

λ³΅μž‘ν•œ type

type AorB = "a" | "b"

union typeμ΄λ‚˜ 쑰건뢀 νƒ€μž…κ³Ό 같이 μ’€ 더 λ³΅μž‘ν•œ type을 μœ„ν•΄μ„œλŠ” interfaceκ°€ μ‚¬μš©λ  수 μ—†λ‹€. type은 ν™œμš©μ„±μ΄ interface보닀 λ†’λ‹€κ³  ν•  수 μžˆλ‹€.

보강

interface IState {
  name: string
  capital: string
}

interface IState {
  population: number
}

interfaceλŠ” typeκ³ΌλŠ” λ‹€λ₯΄κ²Œ 속성을 같은 μ΄λ¦„μ˜ interface을 μ„ μ–Έν•΄ ν™•μž₯ν•  수 μžˆλŠ” νŠΉμ§•μ„ κ°€μ§„λ‹€. 이것을 ν†΅ν•΄μ„œ μ‹€μ œλ‘œ μš°λ¦¬κ°€ μ‚¬μš©ν•˜λŠ” λ‚΄μž₯ λ©”μ†Œλ“œλ“€μ˜ μ •μ˜κ°€ 버전 λ³„λ‘œ ν™•μž₯λ˜μ–΄ 적용되고 μžˆλ‹€.

πŸ€” κ·Έλž˜μ„œ 기쀀은 μ–΄λ–€ κ²ƒμΌκΉŒ?

μ‚¬μš©ν•  λ•Œ 기쀀은 λ¨Όμ € μ‚¬μš©ν•  λ•Œ 일관성을 μœ μ§€ν•΄μ•Ό ν•œλ‹€λŠ” 점이닀. typeκ³Ό interfaceλŠ” 곡톡점이 많기 λ•Œλ¬Έμ— λ‘˜ λ‹€ κ°€λŠ₯ν•œ κ²½μš°κ°€ λ§Žλ‹€. κ·Έλ ‡μ§€λ§Œ type을 μ“°λ‹€κ°€ interfaceλ₯Ό μ“°λŠ” 것이 μ•„λ‹ˆλΌ ν•˜λ‚˜λ‘œ μ •ν•΄μ„œ μΌκ΄€λ˜κ²Œ μž‘μ„±ν•˜λŠ” μ½”λ“œμŠ€νƒ€μΌμ΄ μ€‘μš”ν•˜λ‹€.

각각의 차이점을 κ³ λ €ν•΄μ„œ λ³΅μž‘ν•œ νƒ€μž…μ€ type을 μ‚¬μš©ν•˜κ³  보강이 ν•„μš”ν•œ κ²½μš°μ—λŠ” interfaceλ₯Ό μ΄μš©ν•΄ APIλ₯Ό μ •μ˜ν•  λ•Œ μ‚¬μš©ν•  수 μžˆλ‹€.

[μ°Έμ‘°]

μ΄νŽ™ν‹°λΈŒ νƒ€μž…μŠ€ν¬λ¦½νŠΈ

@choi2021
맀일의 μ‹œν–‰μ°©μ˜€λ₯Ό κΈ°λ‘ν•˜λŠ” κ°œλ°œμΌμ§€μž…λ‹ˆλ‹€.