Among Us - Crewmates
 

[TypeScript] 17. ๋ณ€๊ฒฝ ๊ด€๋ จ๋œ ์˜ค๋ฅ˜ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด readonly ์‚ฌ์šฉํ•˜๊ธฐ

728x90

๐Ÿ“š Effective TypeScript ์ฑ…์„ ์ฝ๊ณ  ๊ณต๋ถ€ํ•œ ๋‚ด์šฉ์„ ์ •๋ฆฌํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค.

๋ณ€๊ฒฝ ๊ด€๋ จ๋œ ์˜ค๋ฅ˜ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด readonly ์‚ฌ์šฉํ•˜๊ธฐ

function arraySum(arr: number[]) {
  let sum = 0, num;
  while ((num = arr.pop()) !== undefined) {
    sum += num;
  }
  return sum;
}
function printTriangles(n: number) {
  const nums = [];
  for (let i = 0; i < n; i++) {
    nums.push(i);
    console.log(arraySum(nums));
  }
}

arraySum ํ•จ์ˆ˜

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

ํ•˜์ง€๋งŒ ์˜ค๋ฅ˜์˜ ๋ฒ”์œ„๋ฅผ ์ขํžˆ๊ธฐ ์œ„ํ•ด, readonly ์ ‘๊ทผ ์ œ์–ด์ž๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ arraySum์ด ๋ฐฐ์—ด์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์„ ์–ธ์„ ํ•œ๋‹ค.

function arraySum(arr: readonly number[]) {
  let sum = 0, num;
  while ((num = arr.pop()) !== undefined) {
                 // ~~~ 'pop' does not exist on type 'readonly number[]'
    sum += num;
  }
  return sum;
}

readonly number[] ๋Š” โ€˜ํƒ€์ž…โ€™์ด๊ณ , number[] ์™€ ๊ตฌ๋ถ„๋˜๋Š” ํŠน์ง•๋“ค์ด ์žˆ๋‹ค.

  • ๋ฐฐ์—ด์˜ ์š”์†Œ๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ์ง€๋งŒ, ์“ธ ์ˆ˜๋Š” ์—†๋‹ค.
  • length๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ์ง€๋งŒ, ๋ฐ”๊ฟ€ ์ˆ˜ ์—†๋‹ค.
  • ๋ฐฐ์—ด์„ ๋ณ€๊ฒฝํ•˜๋Š” pop์„ ๋น„๋กฏํ•œ ๋‹ค๋ฅธ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์—†๋‹ค.

number[]๋Š” readonly number[] ๋ณด๋‹ค ๊ธฐ๋Šฅ์ด ๋งŽ๊ธฐ ๋•Œ๋ฌธ์—, ์„œ๋ธŒํƒ€์ž…์ด ๋œ๋‹ค.

๋”ฐ๋ผ์„œ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ๋ฐฐ์—ด์„ readonly ๋ฐฐ์—ด์— ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๊ทธ ๋ฐ˜๋Œ€๋Š” ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

const a: number[] = [1, 2, 3];
const b: readonly number[] = a;
const c: number[] = b;
   // ~ Type 'readonly number[]' is 'readonly' and cannot be
   //   assigned to the mutable type 'number[]'

๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ readonly๋กœ ์„ ์–ธํ•  ๊ฒฝ์šฐ

  • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ํ•จ์ˆ˜ ๋‚ด์—์„œ ๋ณ€๊ฒฝ์ด ์ผ์–ด๋‚˜๋Š”์ง€ ์ฒดํฌํ•œ๋‹ค.
  • ํ˜ธ์ถœํ•˜๋Š” ์ชฝ์—์„œ๋Š” ํ•จ์ˆ˜๊ฐ€ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๋ณด์žฅ์„ ๋ฐ›๊ฒŒ ๋œ๋‹ค.
  • ํ˜ธ์ถœํ•˜๋Š” ์ชฝ์—์„œ ํ•จ์ˆ˜๊ฐ€ readonly ๋ฐฐ์—ด์„ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋„ฃ์„ ์ˆ˜๋„ ์žˆ๋‹ค.

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ๋Š” ๋ช…์‹œ์ ์œผ๋กœ ์–ธ๊ธ‰ํ•˜์ง€ ์•Š๋Š” ํ•œ, ํ•จ์ˆ˜๊ฐ€ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ๊ฐ€์ •ํ•œ๋‹ค.

  • ํ•˜์ง€๋งŒ ์•”๋ฌต์ ์ธ ๋ฐฉ๋ฒ•์€ ํƒ€์ž… ์ฒดํฌ์— ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ด
  • ๋ช…์‹œ์ ์ธ ๋ฐฉ๋ฒ•์ด ์ข‹๋‹ค !

printTriangles๊ฐ€ ์ด์ œ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•œ๋‹ค.

function arraySum(arr: readonly number[]) {
  let sum = 0;
  for (const num of arr) {
    sum += num;
  }
  return sum;
}
> printTriangles(5)
0
1
3
6
10

ํ•จ์ˆ˜๊ฐ€ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, readonly๋กœ ์„ ์–ธํ•ด์•ผ ํ•œ๋‹ค.

  • ์žฅ์ 
    • ๋„“์€ ํƒ€์ž…์œผ๋กœ ํ˜ธ์ถœ ๊ฐ€๋Šฅ
    • ์˜๋„์น˜ ์•Š์€ ๋ณ€๊ฒฝ์ด ๋ฐฉ์ง€๋œ๋‹ค.
    • ์ง€์—ญ ๋ณ€์ˆ˜์™€ ๊ด€๋ จ๋œ ๋ชจ๋“  ์ข…๋ฅ˜์˜ ๋ณ€๊ฒฝ ์˜ค๋ฅ˜ ๋ฐฉ์ง€ ๊ฐ€๋Šฅ
  • ๋‹จ์  (๊ตณ์ด ์ฐพ์•„๋ณด๋Š” ๋‹จ์ )
    • ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ readonly๋กœ ์„ ์–ธ๋˜์ง€ ์•Š์€ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•  ๊ฒฝ์šฐ๋„ ์žˆ๋‹ค.
      • ํ•˜์ง€๋งŒ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ ๋„ ์ œ์–ด๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด readonly๋กœ ์„ ์–ธํ•˜๋ฉด ๋œ๋‹ค.
    • ์–ด๋–ค ํ•จ์ˆ˜๋ฅผ readonly๋กœ ๋งŒ๋“ค๋ฉด, ๊ทธ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋‹ค๋ฅธ ํ•จ์ˆ˜๋„ ๋ชจ๋‘ readonly๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.
      • ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋ช…ํ™•ํžˆ ํ•˜๊ณ  ํƒ€์ž… ์•ˆ์ •์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ผญ ๋‹จ์ ์ด๋ผ๊ณ  ๋ณผ ์ˆ˜๋Š” ์—†๋‹ค.

์—ฐ์†๋œ ํ–‰์— ๋Œ€ํ•ด ๋นˆ ์ค„์„ ๊ธฐ์ค€์œผ๋กœ ๋‹จ๋ฝ์„ ๊ตฌ๋ถ„ํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ
function parseTaggedText(lines: string[]): string[][] {
  const paragraphs: string[][] = [];
  const currPara: string[] = [];

  const addParagraph = () => {
    if (currPara.length) {
      paragraphs.push(currPara);
      currPara.length = 0;  // Clear the lines
    }
  };

  for (const line of lines) {
    if (!line) {
      addParagraph();
    } else {
      currPara.push(line);
    }
  }
  addParagraph();
  return paragraphs;
}
[ [], [], [] ]
  • ๋ฌธ์ œ์ 
    • ๋ณ„์นญ๊ณผ ๋ณ€๊ฒฝ์„ ๋™์‹œ์— ์‚ฌ์šฉํ•ด ๋ฐœ์ƒ

      paragraphs.push(currPara);

      • currPara์˜ ๋‚ด์šฉ์ด ์‚ฝ์ž…๋˜์ง€ ์•Š๊ณ  ๋ฐฐ์—ด์˜ ์ฐธ์กฐ๊ฐ€ ์‚ฝ์ž…
      • currPara์— ๋“ค์–ด ์žˆ๋˜ ๊ฐ’์ด ์ง€์›Œ์ง
      • ๋™์ผํ•œ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•˜๊ณ  ์žˆ๋Š” paragraphs ์š”์†Œ์—๋„ ๋ณ€๊ฒฝ์ด ๋ฐ˜์˜

      โ‡’ ์ฆ‰, ์ƒˆ ๋‹จ๋ฝ์„ paragraphs์— ์‚ฝ์ž…ํ•˜๊ณ  ๋ฐ”๋กœ ์ง€์›Œ๋ฒ„๋ฆผ

currPara๋ฅผ readonly๋กœ ์„ ์–ธํ•˜์—ฌ ์ด๋Ÿฐ ๋™์ž‘์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

์„ ์–ธ์„ ๋ฐ”๊พธ๋Š” ์ฆ‰์‹œ ์ฝ”๋“œ ๋‚ด์—์„œ ๋ช‡ ๊ฐ€์ง€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค.

function parseTaggedText(lines: string[]): string[][] {
  const currPara: readonly string[] = [];
  const paragraphs: string[][] = [];

  const addParagraph = () => {
    if (currPara.length) {
      paragraphs.push(
        currPara
     // ~~~~~~~~ Type 'readonly string[]' is 'readonly' and
     //          cannot be assigned to the mutable type 'string[]'
      );
      currPara.length = 0;  // Clear lines
            // ~~~~~~ Cannot assign to 'length' because it is a read-only 
            // property
    }
  };

  for (const line of lines) {
    if (!line) {
      addParagraph();
    } else {
      currPara.push(line);
            // ~~~~ Property 'push' does not exist on type 'readonly string[]'
    }
  }
  addParagraph();
  return paragraphs;
}

currPara๋ฅผ let ์œผ๋กœ ์„ ์–ธํ•˜๊ณ  ๋ณ€ํ™˜์ด ์—†๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ ๋‘ ์˜ค๋ฅ˜๋ฅผ ์žก์„ ์ˆ˜ ์žˆ๋‹ค.

let currPara: readonly string[] = [];
// ...
currPara = []; // ๋ฐฐ์—ด์„ ๋น„์›€
// ...
currPara = currPara.concat([line]);

  • concat ์€ push ์™€ ๋‹ฌ๋ฆฌ ์›๋ณธ์„ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ  ์ƒˆ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • ์„ ์–ธ๋ถ€๋ฅผ const ์—์„œ let์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  readonly๋ฅผ ์ถ”๊ฐ€ํ•จ์œผ๋กœ์จ ํ•œ ์ชฝ์˜ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ์„ฑ์„ ์˜ฎ๊น€
    • currPara ๋ณ€์ˆ˜๋Š” ๊ฐ€๋ฆฌํ‚ค๋Š” ๋ฐฐ์—ด์„ ์ž์œ ๋กญ๊ฒŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์ง€๋งŒ
      • ๋ฐฐ์—ด ์ž์ฒด๋Š” ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€

์—ฌ์ „ํžˆ ์กด์žฌํ•˜๋Š” paragraphs์— ๋Œ€ํ•œ ์˜ค๋ฅ˜๋ฅผ ๋ฐ”๋กœ์žก๋Š” ๋ฐฉ๋ฒ• 3๊ฐ€์ง€

  1. currPara ๋ณต์‚ฌ๋ณธ ๋งŒ๋“ค๊ธฐ
    paragraphs.push([...currPara]);
    • currPara๋Š” readonly๋กœ ์œ ์ง€, ๋ณต์‚ฌ๋ณธ์€ ์›ํ•˜๋Š”๋Œ€๋กœ ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅ
  1. paragraphs ๋ฅผ readonly string[] ์˜ ๋ฐฐ์—ด๋กœ ๋ณ€๊ฒฝํ•˜๊ธฐ
    const paragraphs: (readonly string[])[] = [];

    readonly string[][]์€

    • readonly ๋ฐฐ์—ด์˜ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ๋ฐฐ์—ด์ด ์•„๋‹ˆ๋ผ
    • ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ๋ฐฐ์—ด์˜ readonly ๋ฐฐ์—ด์ด๋‹ค
  1. ๋ฐฐ์—ด์˜ readonly ์†์„ฑ์„ ์ œ๊ฑฐํ•˜๊ธฐ ์œ„ํ•ด ๋‹จ์–ธ๋ฌธ ์‚ฌ์šฉํ•˜๊ธฐ
    paragraphs.push(currPara as string[]);
    • ๋ฐ”๋กœ ๋‹ค์Œ ๋ฌธ์žฅ์—์„œ currPara๋ฅผ ์ƒˆ ๋ฐฐ์—ด์— ํ• ๋‹นํ•จ
      • ๊ณต๊ฒฉ์ ์ธ ๋‹จ์–ธ๋ฌธ์œผ๋กœ ๋ณด์ด์ง€๋Š” ์•Š์Œ

๐Ÿ’ก
readonly๋Š” ์–•๊ฒŒ(shallow) ๋™์ž‘ํ•œ๋‹ค !
const dates: readonly Date[] = [new Date()];
dates.push(new Date());
   // ~~~~ Property 'push' does not exist on type 'readonly Date[]'
dates[0].setFullYear(2037);  // OK
  • ๊ฐ์ฒด์˜ readonly ๋ฐฐ์—ด์ด ์žˆ๋‹ค๋ฉด, ๊ทธ ๊ฐ์ฒด ์ž์ฒด๋Š” readonly๊ฐ€ ์•„๋‹ˆ๋‹ค.

interface Outer {
  inner: {
    x: number;
  }
}
const o: Readonly<Outer> = { inner: { x: 0 }};
o.inner = { x: 1 };
// ~~~~ Cannot assign to 'inner' because it is a read-only property
o.inner.x = 1;  // OK
type T = Readonly<Outer>;
// Type T = {
//   readonly inner: {
//     x: number;
//   };
// }
  • Readonly ์ œ๋„ˆ๋ฆญ์—๋„ ํ•ด๋‹น๋œ๋‹ค.
    • readonly ์ ‘๊ทผ์ œ์–ด์ž๋Š” x๊ฐ€ ์•„๋‹ˆ๋ผ inner์— ์ ์šฉ๋˜๋Š” ๊ฒƒ

ํ˜„์žฌ ์‹œ์ ์—๋Š” ๊นŠ์€(deep) readonly ํƒ€์ž…์ด ๊ธฐ๋ณธ์œผ๋กœ ์ง€์›๋˜์ง€ ์•Š์Œ


Uploaded by N2T

728x90
๋ฐ˜์‘ํ˜•