Google의 TypeScript Style Guide > Language features > 4.1 Local variable declarations

FeelsBotMan·2024년 12월 2일
0

GTS

목록 보기
4/8
post-thumbnail

Language features

4.1 Local variable declarations

4.1.1 Use const and let

항상 const 또는 let을 사용하여 변수를 선언하자. 변수를 재할당해야 하는 경우가 아니면 기본적으로 const를 사용하고 var는 사용하지 말자. (constlet은 대부분 다른 언어의 변수처럼 블록 스코프이지만, var는 함수 스코프로 이해하기 어려운 버그를 일으킬 수 있다.)
변수는 선언 전에 사용되어서는 안 된다.


4.1.2 One variable per declaration

한 번에 하나의 변수 선언만 하자. (let a = 1, b = 2; 금지)


4.2 Array literals

4.2.1 Do not use the Array constructor

Array() 생성자를 사용을 피하자. 혼란스럽고 모순되는 사용법이 존재하기 때문이다.

Before

const a = new Array(2); // [undefined, undefined]
const b = new Array(2, 3); // [2, 3];

After

const a = [2];
const b = [2, 3];

// new Array(2) 대신
const c = [];
c.length = 2;

// [0, 0, 0, 0, 0]
Array.from<number>({length: 5}).fill(0);

4.2.2 Do not define properties on arrays

배열에 숫자가 아닌 속성을 정의하거나 사용하지 말자(length는 제외).
배열은 본래 순차적인 데이터 저장을 위한 자료구조이기 때문에, 배열의 인덱스(즉, 숫자형 인덱스) 외에 임의의 문자열 속성을 정의하는 것은 혼란을 초래할 수 있다.
대신 Map(또는 Object)을 사용하자.

Before

const arr = [1, 2, 3];
arr.customProperty = "Hello";

console.log(arr.customProperty); // "Hello"
console.log(arr.length); // 3 (길이는 영향을 받지 않음)

for (const key in arr) {
  console.log(key); // "0", "1", "2", "customProperty"(예상치 못한 속성)
}

console.log(Object.keys(arr)); // ["0", "1", "2", "customProperty"]

After

const map = new Map();
map.set("customProperty", "Hello");
map.set(0, 1);
map.set(1, 2);

console.log(map.get("customProperty")); // "Hello"
console.log(map.get(0)); // 1

4.2.3 Using spread syntax

스프레드 문법은 이터러블(iterable) 객체를 펼쳐서 새로운 배열이나 객체를 만들 때 유용하지만, 그 사용에 있어서 몇 가지 주의해야 할 점이 있다.

스프레드 문법은 이터러블만 펼쳐야 한다:
스프레드 문법을 사용할 때 펼칠 값은 이터러블이어야 한다. 이터러블이란 배열, 문자열, 맵, 세트 같은 데이터 구조를 말하며, 그 안의 요소들을 순차적으로 접근할 수 있다.
원시 값(primitive types)인 null, undefined, 숫자, 문자열 등의 값은 스프레드 문법을 사용할 수 없다.

Before

const foo = [7];
const bar = [5, ...(shouldUseFoo && foo)]; // 에러 발생할 수 있음

After

const foo = shouldUseFoo ? [7] : []; // 조건에 따라 배열 선택.
const bar = [5, ...foo]; 

스프레드되는 값은 생성되는 값과 자료형과 구조를 일치하는 것이 권장된다:
Before

const fooStrings = ['a', 'b', 'c'];
const ids = {...fooStrings};  // {0: 'a', 1: 'b', 2: 'c'}

After

const fooStrings = ['a', 'b', 'c'];
const ids = [...fooStrings, 'd', 'e'];  // ['a', 'b', 'c', 'd', 'e']

스프레드 문법은 얕은 복사(shallow copy)를 수행한다.

얕은 복사는 객체나 배열의 1단계 깊이까지만 복사한다. 따라서 다음과 같은 상황이 발생한다:

  • 기본 자료형 (primitive types):
    • 숫자, 문자열, 불리언 등은 값 자체가 복사된다.
    • 원본과 복사본이 독립적으로 동작.
  • 참조 자료형 (reference types):
    • 배열, 객체 등은 메모리 주소(참조)만 복사된다.
    • 복사본과 원본이 같은 참조를 공유하므로, 복사본에서 수정하면 원본도 영향을 받는다.
const arr = [1, 2, 3];  // 기본 자료형만 포함된 배열
const spreadArr = [...arr];
spreadArr[0] = 99;

console.log(arr);       // [1, 2, 3] (원본 불변)
console.log(spreadArr); // [99, 2, 3] (복사본만 변경됨)

const arr = [1, 2, { a: 3 }];  // 참조 자료형이 포함된 배열
const spreadArr = [...arr];
spreadArr[2].a = 10;

console.log(arr);       // [1, 2, { a: 10 }] (원본도 영향받음)
console.log(spreadArr); // [1, 2, { a: 10 }] (복사본도 동일)

4.2.4 Array destructuring

구조 분해 할당은 코드의 가독성을 높이고, 변수를 더 쉽게 추출할 수 있도록 돕는 유용한 기능이다. 하지만 올바르게 사용하는 방법이 중요하다.

배열을 구조 분해하여 변수에 값을 할당하는 방법은 다음과 같다:

const [a, b, c, ...rest] = generateResults();
let [, b,, d] = someArray;

배열에 기본값을 설정하는 방법:
Before

function destructuring([a, b] = [4, 2]) {}

After

function destructured([a = 4, b = 2] = []) {}

배열 대신 객체 구조 분해를 사용하는 것이 더 좋은 경우가 많다. 배열 구조 분해는 순서대로 값을 추출하므로, 배열의 순서가 바뀌면 코드가 깨질 수 있다. 반면, 객체 구조 분해는 키 이름으로 값을 추출하기 때문에 순서에 의존하지 않아 더 안전하고 직관적이다.

const { name, age, location } = user;

4.3 Object literals

4.3.1 Do not use the Object constructor

Object() 생성자는 객체를 생성하는 데 사용될 수 있지만, 객체 리터럴 {}을 사용하는 것이 훨씬 더 간결하고 명확하다.

Before

let obj = new Object();
obj.a = 1;
obj.b = 2;

After

let obj = { a: 1, b: 2 };
  • 더 직관적이고, 코드가 짧고 읽기 쉬워진다.

4.3.2 Iterating objects

for (... in ...)는 객체의 모든 열거 가능한(enumerable) 속성을 순회한다.
여기에는 해당 객체의 직접 속성뿐만 아니라 프로토타입 체인에서 상속된 속성도 포함될 수 있으므로 주의하자.

Before

const someObj = { a: 1, b: 2 };

Object.prototype.c = 3; // 프로토타입에 추가된 속성

for (const key in someObj) {
  console.log(key); // 출력: "a", "b", "c" (예상치 못한 속성 포함)
}

After

for (const key in someObj) {
  if (someObj.hasOwnProperty(key)) {
    console.log(key); // 출력: "a", "b" (프로토타입 속성 제외)
  }
}
  • hasOwnProperty는 해당 속성이 객체의 직접 속성인지 확인
for (const key of Object.keys(someObj)) {
  console.log(key); // 출력: "a", "b"
}
  • Object.keys()는 객체의 직접 열거 가능한 속성의 키만 반환
for (const [key, value] of Object.entries(someObj)) {
  console.log(key, value); // 출력: "a 1", "b 2"
}
  • Object.entries()는 [키, 값] 쌍의 배열을 반환

4.3.3 Using spread syntax

스프레드 문법은 기존 객체의 얕은 복사(shallow copy)를 생성한다.
중복 키가 있는 경우, 나중에 정의된 값이 기존 값을 덮어쓴다.

const foo = { num: 1 };

const foo2 = {
  ...foo,
  num: 5,
}; // 기존 foo의 내용을 복사하고, num을 5로 덮어씀.

const foo3 = {
  num: 5,
  ...foo,
}; // foo의 내용을 복사하면서 기존 num: 5를 덮어씀.

console.log(foo2.num); // 출력: 5
console.log(foo3.num); // 출력: 1

객체를 생성할 때 오직 객체만 스프레드하자:
배열과 기본형(null과 undefined 포함)을 스프레드 해서는 안된다.

const fooStrings = ['a', 'b', 'c'];
const ids = {...fooStrings};  // {0: 'a', 1: 'b', 2: 'c'}

기본 Object 프로토타입이 아닌 다른 프로토타입을 갖는 객체(예: 클래스 정의, 클래스 인스턴스, 함수)의 스프레드는 주의해야 한다:
스프레드 문법은 객체의 열거 가능한(enumerable) 비-프로토타입(non-prototype) 속성만 복사한다. (Object.keys(obj)로 반환되는 속성들)
커스텀 프로토타입을 가지는 객체는 프로토타입 체인에 있는 속성(메서드 등)은 복사되지 않는다.


4.3.4 Computed property names

계산된 프로퍼티명(computed property names)를 사용하면 더 동적으로 키를 정의할 수 있다:
JavaScript에서 객체 리터럴에서의 속성 정의는 보통 정적인 문자열이나 식별자를 사용하지만, Computed property names는 객체의 키를 런타임 시 계산된 값으로 설정할 때 사용된다.

const dynamicKey = 'age';
const obj = {
  [dynamicKey]: 25, // 'age': 25
};

console.log(obj.age); // 25

계산된 속성이 심볼(예: [Symbol.iterator])이 아닌 한, dict 스타일(따옴표로 묶인) 키로 간주된다. (즉, 따옴표로 묶이지 않은 키와 혼합하지 말자).


4.3.5 Object destructuring

구조 분해는 단순한 한 단계 수준에서만 사용이 권장된다:
중첩된 구조 분해는 가독성을 해치고, 매개변수의 복잡성을 증가시킨다. 중첩된 구조를 분해해야 한다면, 함수를 나누어 처리하거나 별도로 객체를 추출하여 사용하는 것이 낫다.
Before

function nestedTooDeeply({x: {num, str}}: {x: Options}) {}

After

interface Options {
  num?: number;
  str?: string;
}

function destructured({num, str = 'default'}: Options = {}) {}

기본값은 구조 분해의 왼쪽에서 명시적으로 설정한다.
Before

function destructured({num, str}: Options = {num: 42, str: 'default'}) {
  console.log(num, str);
}

After

function destructured({num = 42, str = 'default'}: Options = {}) {
  console.log(num, str);
}

참고자료

Google TypeScript Style Guide

GitHub - google/gts: ☂️ TypeScript style guide, formatter, and linter.

Typescript Google Code Style Part 1
Typescript Google Code Style Part 2
Typescript Google Code Style Part 3

ts.dev - TypeScript style guide

profile
안드로이드 페페

0개의 댓글