Skip to content

Latest commit

ย 

History

History
306 lines (249 loc) ยท 9.8 KB

unions-and-intersections.md

File metadata and controls

306 lines (249 loc) ยท 9.8 KB
title layout permalink oneline
Unions and Intersection Types
docs
/docs/handbook/unions-and-intersections.html
How to use unions and intersection types in TypeScript

์ง€๊ธˆ๊นŒ์ง€, ํ•ธ๋“œ๋ถ์€ ์›์ž ๊ฐ์ฒด์˜ ํƒ€์ž…๋“ค์„ ๋‹ค๋ค„์™”์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ๋” ๋งŽ์€ ํƒ€์ž…์„ ๋ชจ๋ธ๋งํ• ์ˆ˜๋ก ์ฒ˜์Œ๋ถ€ํ„ฐ ํƒ€์ž…์„ ๋งŒ๋“ค์–ด๋‚ด๊ธฐ๋ณด๋‹ค๋Š” ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ํƒ€์ž…์„ ๊ตฌ์„ฑํ•˜๊ฑฐ๋‚˜ ๊ฒฐํ•ฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋“ค์„ ์ฐพ๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ต์ฐจ ํƒ€์ž…๊ณผ ์œ ๋‹ˆ์–ธ ํƒ€์ž…์€ ํƒ€์ž…์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.

์œ ๋‹ˆ์–ธ ํƒ€์ž… (Union Types)

๊ฐ€๋”, number๋‚˜ string์„ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๊ธฐ๋Œ€ํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋‹ค์Œ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ์ž…๋‹ˆ๋‹ค:

/**
 * ๋ฌธ์ž์—ด์„ ๋ฐ›๊ณ  ์™ผ์ชฝ์— "padding"์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
 * ๋งŒ์•ฝ 'padding'์ด ๋ฌธ์ž์—ด์ด๋ผ๋ฉด, 'padding'์€ ์™ผ์ชฝ์— ๋”ํ•ด์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
 * ๋งŒ์•ฝ 'padding'์ด ์ˆซ์ž๋ผ๋ฉด, ๊ทธ ์ˆซ์ž๋งŒํผ์˜ ๊ณต๋ฐฑ์ด ์™ผ์ชฝ์— ๋”ํ•ด์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
 */
function padLeft(value: string, padding: any) {
  if (typeof padding === "number") {
    return Array(padding + 1).join(" ") + value;
  }
  if (typeof padding === "string") {
    return padding + value;
  }
  throw new Error(`Expected string or number, got '${padding}'.`);
}

padLeft("Hello world", 4); // "Hello world"๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์œ„ ์˜ˆ์ œ์—์„œ padLeft์˜ ๋ฌธ์ œ๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜ padding์ด any ํƒ€์ž…์œผ๋กœ ๋˜์–ด์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฆ‰, number๋‚˜ string ๋‘˜ ๋‹ค ์•„๋‹Œ ์ธ์ˆ˜๋กœ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๊ณ , TypeScript๋Š” ์ด๋ฅผ ๊ดœ์ฐฎ๋‹ค๊ณ  ๋ฐ›์•„๋“ค์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

declare function padLeft(value: string, padding: any): string;
// ---์ƒ๋žต---
// ์ปดํŒŒ์ผ ํƒ€์ž„์—๋Š” ํ†ต๊ณผํ•˜์ง€๋งŒ, ๋Ÿฐํƒ€์ž„์—๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
let indentedString = padLeft("Hello world", true);

์ „ํ†ต์ ์ธ ๊ฐ์ฒด์ง€ํ–ฅ ์ฝ”๋“œ์—์„œ, ํƒ€์ž…์˜ ๊ณ„์ธต์„ ์ƒ์„ฑํ•˜์—ฌ ๋‘ ํƒ€์ž…์„ ์ถ”์ƒํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋” ๋ช…์‹œ์ ์ผ ์ˆ˜๋Š” ์žˆ์ง€๋งŒ, ์กฐ๊ธˆ ๊ณผํ•˜๋‹ค๊ณ  ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด ๋ฐฉ๋ฒ•์˜ padLeft์—์„œ ์ข‹์€ ์  ์ค‘ ํ•˜๋‚˜๋Š” ์›์‹œ ๊ฐ’์„ ๋‹จ์ง€ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฆ‰ ์‚ฌ์šฉ๋ฒ•์ด ๊ฐ„๋‹จํ•˜๊ณ  ๊ฐ„๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์ด ์ƒˆ๋กœ์šด ๋ฐฉ๋ฒ•์€ ๋‹จ์ง€ ๋‹ค๋ฅธ ๊ณณ์— ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•  ๋•Œ ๋„์›€์ด ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

any ๋Œ€์‹ ์—, _์œ ๋‹ˆ์–ธ ํƒ€์ž…_์„ ๋งค๊ฐœ๋ณ€์ˆ˜ padding์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

// @errors: 2345
/**
 * ๋ฌธ์ž์—ด์„ ๋ฐ›๊ณ  ์™ผ์ชฝ์— "padding"์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
 * ๋งŒ์•ฝ 'padding'์ด ๋ฌธ์ž์—ด์ด๋ผ๋ฉด, 'padding'์€ ์™ผ์ชฝ์— ๋”ํ•ด์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
 * ๋งŒ์•ฝ 'padding'์ด ์ˆซ์ž๋ผ๋ฉด, ๊ทธ ์ˆซ์ž๋งŒํผ์˜ ๊ณต๋ฐฑ์ด ์™ผ์ชฝ์— ๋”ํ•ด์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
 */
function padLeft(value: string, padding: string | number) {
  // ...
}

let indentedString = padLeft("Hello world", true);

์œ ๋‹ˆ์–ธ ํƒ€์ž…์€ ์—ฌ๋Ÿฌ ํƒ€์ž… ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋  ์ˆ˜ ์žˆ๋Š” ๊ฐ’์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์„ธ๋กœ ๋ง‰๋Œ€ (|)๋กœ ๊ฐ ํƒ€์ž…์„ ๊ตฌ๋ถ„ํ•˜์—ฌ, number | string | boolean์€ ๊ฐ’์˜ ํƒ€์ž…์ด number, string ํ˜น์€ boolean์ด ๋  ์ˆ˜ ์žˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

๊ณตํ†ต ํ•„๋“œ๋ฅผ ๊ฐ–๋Š” ์œ ๋‹ˆ์–ธ (Unions with Common Fields)

์œ ๋‹ˆ์–ธ ํƒ€์ž…์ธ ๊ฐ’์ด ์žˆ์œผ๋ฉด, ์œ ๋‹ˆ์–ธ์— ์žˆ๋Š” ๋ชจ๋“  ํƒ€์ž…์— ๊ณตํ†ต์ธ ๋ฉค๋ฒ„๋“ค์—๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// @errors: 2339

interface Bird {
  fly(): void;
  layEggs(): void;
}

interface Fish {
  swim(): void;
  layEggs(): void;
}

declare function getSmallPet(): Fish | Bird;

let pet = getSmallPet();
pet.layEggs();

// ๋‘ ๊ฐœ์˜ ์ž ์žฌ์ ์ธ ํƒ€์ž… ์ค‘ ํ•˜๋‚˜์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
pet.swim();

์—ฌ๊ธฐ์„œ ์œ ๋‹ˆ์–ธ ํƒ€์ž…์€ ์•ฝ๊ฐ„ ๊นŒ๋‹ค๋กœ์šธ ์ˆ˜ ์žˆ์œผ๋‚˜, ์ต์ˆ™ํ•ด์ง€๋Š” ๋ฐ ์•ฝ๊ฐ„์˜ ์ง๊ด€๋งŒ ์žˆ์œผ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๊ฐ’์ด A | B ํƒ€์ž…์„ ๊ฐ–๊ณ  ์žˆ์œผ๋ฉด, ํ™•์‹ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ ๊ทธ ๊ฐ’์ด A์™€B ๋‘˜ ๋‹ค ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋ฉค๋ฒ„๋“ค์„ ๊ฐ–๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ๋ฟ์ž…๋‹ˆ๋‹ค. ์ด ์˜ˆ์ œ์—์„œ, Bird๋Š” fly๋ผ๋Š” ๋ฉค๋ฒ„๋ฅผ ๊ฐ–๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. Bird | Fish๋กœ ํƒ€์ž…์ด ์ง€์ •๋œ ๋ณ€์ˆ˜๊ฐ€ fly ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”์ง€ ํ™•์‹ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋ณ€์ˆ˜๊ฐ€ ์‹ค์ œ๋กœ ๋Ÿฐํƒ€์ž„์— Fish์ด๋ฉด, pet.fly()๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์€ ์˜ค๋ฅ˜์ž…๋‹ˆ๋‹ค.

์œ ๋‹ˆ์–ธ ๊ตฌ๋ณ„ํ•˜๊ธฐ (Discriminating Unions)

์œ ๋‹ˆ์–ธ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ ์žˆ์–ด์„œ ์ผ๋ฐ˜์ ์ธ ๊ธฐ์ˆ ์€ TypeScript๊ฐ€ ํ˜„์žฌ ๊ฐ€๋Šฅํ•œ ํƒ€์ž… ์ถ”๋ก ์˜ ๋ฒ”์œ„๋ฅผ ์ขํ˜€๋‚˜๊ฐ€๊ฒŒ ํ•ด์ค„ ์ˆ˜ ์žˆ๋Š” ๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…์„ ๊ฐ–๋Š” ๋‹จ์ผ ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ํ•˜๋‚˜์˜ ๊ณตํ†ต ํ•„๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์„ธ ๊ฐ€์ง€ ํƒ€์ž…์˜ ์œ ๋‹ˆ์–ธ์„ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

type NetworkLoadingState = {
  state: "loading";
};

type NetworkFailedState = {
  state: "failed";
  code: number;
};

type NetworkSuccessState = {
  state: "success";
  response: {
    title: string;
    duration: number;
    summary: string;
  };
};

// ์œ„ ํƒ€์ž…๋“ค ์ค‘ ๋‹จ ํ•˜๋‚˜๋ฅผ ๋Œ€ํ‘œํ•˜๋Š” ํƒ€์ž…์„ ๋งŒ๋“ค์—ˆ์ง€๋งŒ,
// ๊ทธ๊ฒƒ์ด ๋ฌด์—‡์— ํ•ด๋‹นํ•˜๋Š”์ง€ ์•„์ง ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
type NetworkState =
  | NetworkLoadingState
  | NetworkFailedState
  | NetworkSuccessState;

์œ„ ํƒ€์ž…๋“ค์€ ๋ชจ๋‘ state๋ผ๋Š” ํ•„๋“œ๋ฅผ ๊ฐ–๊ณ  ์žˆ์œผ๋ฉฐ, ๊ทธ๋“ค ๊ฐ์ž๋งŒ์˜ ํ•„๋“œ๋„ ๊ฐ–๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค:

NetworkLoadingState NetworkFailedState NetworkSuccessState
state state state
code response

state ํ•„๋“œ๊ฐ€ NetworkState ์•ˆ์˜ ๋ชจ๋“  ํƒ€์ž…์— ๊ณตํ†ต์œผ๋กœ ์กด์žฌํ•œ๋‹ค๋Š” ์ ์„ ์•ˆ๋‹ค๋ฉด - ์กด์žฌ ์—ฌ๋ถ€๋ฅผ ์ฒดํฌํ•˜์ง€ ์•Š๊ณ ๋„ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…์œผ๋กœ์„œ state๋ฅผ ๊ฐ–๊ณ  ์žˆ๋‹ค๋ฉด, state์˜ ๊ฐ’์€ ๋Œ€์‘ํ•˜๋Š” ๋™์ผํ•œ ๋ฌธ์ž์—ด๊ณผ ๋Œ€์กฐ๋˜๊ณ  TypeScript๋Š” ํ˜„์žฌ ์–ด๋–ค ํƒ€์ž…์ด ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋Š”์ง€ ์•Œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

NetworkLoadingState NetworkFailedState NetworkSuccessState
"loading" "failed" "success"

์ด ๊ฒฝ์šฐ, ๋Ÿฐํƒ€์ž„์— ๋‚˜ํƒ€๋‚˜๋Š” ํƒ€์ž…์˜ ๋ฒ”์œ„๋ฅผ ์ขํžˆ๊ธฐ ์œ„ํ•˜์—ฌ switch๋ฌธ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// @errors: 2339
type NetworkLoadingState = {
  state: "loading";
};

type NetworkFailedState = {
  state: "failed";
  code: number;
};

type NetworkSuccessState = {
  state: "success";
  response: {
    title: string;
    duration: number;
    summary: string;
  };
};
// ---์ƒ๋žต---
type NetworkState =
  | NetworkLoadingState
  | NetworkFailedState
  | NetworkSuccessState;

function networkStatus(state: NetworkState): string {
  // ํ˜„์žฌ TypeScript๋Š” ์…‹ ์ค‘ ์–ด๋–ค ๊ฒƒ์ด
  // state๊ฐ€ ๋  ์ˆ˜ ์žˆ๋Š” ์ž ์žฌ์ ์ธ ํƒ€์ž…์ธ์ง€ ์•Œ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

  // ๋ชจ๋“  ํƒ€์ž…์— ๊ณต์œ ๋˜์ง€ ์•Š๋Š” ํ”„๋กœํผํ‹ฐ์— ์ ‘๊ทผํ•˜๋ ค๋Š” ์‹œ๋„๋Š”
  // ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.
  state.code;

  // state์— swtich๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ, TypeScript๋Š” ์ฝ”๋“œ ํ๋ฆ„์„ ๋ถ„์„ํ•˜๋ฉด์„œ
  // ์œ ๋‹ˆ์–ธ ํƒ€์ž…์„ ์ขํ˜€๋‚˜๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  switch (state.state) {
    case "loading":
      return "Downloading...";
    case "failed":
      // ์—ฌ๊ธฐ์„œ ํƒ€์ž…์€ NetworkFailedState์ผ ๊ฒƒ์ด๋ฉฐ,
      // ๋”ฐ๋ผ์„œ `code` ํ•„๋“œ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
      return `Error ${state.code} downloading`;
    case "success":
      return `Downloaded ${state.response.title} - ${state.response.summary}`;
  }
}

๊ต์ฐจ ํƒ€์ž… (Intersection Types)

๊ต์ฐจ ํƒ€์ž…์€ ์œ ๋‹ˆ์–ธ ํƒ€์ž…๊ณผ ๋ฐ€์ ‘ํ•œ ๊ด€๋ จ์ด ์žˆ์ง€๋งŒ, ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์€ ๋งค์šฐ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ๊ต์ฐจ ํƒ€์ž…์€ ์—ฌ๋Ÿฌ ํƒ€์ž…์„ ํ•˜๋‚˜๋กœ ๊ฒฐํ•ฉํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด ํƒ€์ž…์„ ํ•ฉ์ณ ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์„ ๋ชจ๋‘ ๊ฐ€์ง„ ๋‹จ์ผ ํƒ€์ž…์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, Person & Serializable & Loggable์€ Person ๊ณผ Serializable ๊ทธ๋ฆฌ๊ณ  Loggable์ž…๋‹ˆ๋‹ค. ์ฆ‰, ์ด ํƒ€์ž…์˜ ๊ฐ์ฒด๋Š” ์„ธ ๊ฐ€์ง€ ํƒ€์ž…์˜ ๋ชจ๋“  ๋ฉค๋ฒ„๋ฅผ ๊ฐ–๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์ผ๊ด€๋œ ์—๋Ÿฌ๋ฅผ ๋‹ค๋ฃจ๋Š” ์—ฌ๋Ÿฌ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ์žˆ๋‹ค๋ฉด ํ•ด๋‹น ์—๋Ÿฌ ํ•ธ๋“ค๋ง์„ ๋ถ„๋ฆฌํ•˜์—ฌ ํ•˜๋‚˜์˜ ์‘๋‹ต ํƒ€์ž…์— ๋Œ€์‘ํ•˜๋Š” ๊ฒฐํ•ฉ๋œ ์ž์ฒด ํƒ€์ž…์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

interface ErrorHandling {
  success: boolean;
  error?: { message: string };
}

interface ArtworksData {
  artworks: { title: string }[];
}

interface ArtistsData {
  artists: { name: string }[];
}

// ์ด ์ธํ„ฐํŽ˜์ด์Šค๋“ค์€
// ํ•˜๋‚˜์˜ ์—๋Ÿฌ ํ•ธ๋“ค๋ง๊ณผ ์ž์ฒด ๋ฐ์ดํ„ฐ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

type ArtworksResponse = ArtworksData & ErrorHandling;
type ArtistsResponse = ArtistsData & ErrorHandling;

const handleArtistsResponse = (response: ArtistsResponse) => {
  if (response.error) {
    console.error(response.error.message);
    return;
  }

  console.log(response.artists);
};

๊ต์ฐจ๋ฅผ ํ†ตํ•œ ๋ฏน์Šค์ธ (Mixins via Intersections)

๊ต์ฐจ๋Š” ๋ฏน์Šค์ธ ํŒจํ„ด์„ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

class Person {
  constructor(public name: string) {}
}

interface Loggable {
  log(name: string): void;
}

class ConsoleLogger implements Loggable {
  log(name: string) {
    console.log(`Hello, I'm ${name}.`);
  }
}

// ๋‘ ๊ฐ์ฒด๋ฅผ ๋ฐ›์•„ ํ•˜๋‚˜๋กœ ํ•ฉ์นฉ๋‹ˆ๋‹ค.
function extend<First extends {}, Second extends {}>(
  first: First,
  second: Second
): First & Second {
  const result: Partial<First & Second> = {};
  for (const prop in first) {
    if (first.hasOwnProperty(prop)) {
      (result as First)[prop] = first[prop];
    }
  }
  for (const prop in second) {
    if (second.hasOwnProperty(prop)) {
      (result as Second)[prop] = second[prop];
    }
  }
  return result as First & Second;
}

const jim = extend(new Person("Jim"), ConsoleLogger.prototype);
jim.log(jim.name);