TS스터디 이펙티브 item17~18

온호성·2023년 4월 7일
1

🤖item 17 변경 관련 오류 방지를 위해 readonly 사용하기

배열을 인자로 받아 연산하는 함수는 인자가 참조하는 배열의 원본을 바꿀 수 있기 때문에 사이드 이펙트를 원치 않는 함수에서는 readonly 접근 제어자를 사용할 수 있다.

readonly number[]와 number[]의 차이

  • 읽을수 있으나 쓸 수 없다.
  • length를 읽을 수 있으나 바꿀 수 없다.
  • pop등을 비롯한 배열 변경 다른 메서드들 호출 할수 없다.
  • number[]는 readonly number[]보다 기능이 많기 때문에, readonly number[]의 서브타입이 된다.
    -> number[] 를 readonly number[] 에 할당할 수는 있지만, readonly number[] 를 number[]에 할당할 수 없다.
const a: number[] = [1, 2, 3];
const b: readonly number[] = a; // 정상
const c: number[] = b; // 오류

매개변수를 readonly로 선언하면

  • ts는 매개변수가 함수 내에서 변경이 일어나는지 체크한다.
  • 함수 호출자는 함수가 매개변수를 변경하지 않는다는 보상을 받는다.
  • 함수 호출자가 함수에게 readonly 배열을 매개변수로 넘길 수 있다.
const arr = [ 1, 2, 3, 4 ];

const sum = (arr: readonly number []) => { // readonly 매개변수
  return arr.reduce((prev, cur) => prev + cur, 0);
}

sum(arr);

만약 함수가 매개변수를 변경하지 않고도 제어가 가능하다면 readonly로 선언하면 된다. 그런데 어떤 함수를 readonly로 만들면, 그 함수를 호출하는 다른 함수도 모두 readonly로 만들어야 한다.

const와 readonly의 차이점

const와 readonly는 모두 값을 변경할 수 없는 변수를 만드는 데 사용되는 ts의 키워드이다.

  • 상수의 타입:
    const 키워드로 선언된 변수는 값이 할당될 때 타입을 추론하므로 상수의 타입을 명시적으로 선언해야 한다. 반면, readonly 키워드로 선언된 속성은 변수 타입이 이미 지정되어 있기 때문에 타입 선언이 필요하지 않다.

  • 불변성의 범위:
    const는 변수 자체가 변경할 수 없다. 그러나 변수에 할당된 객체는 변경할 수 있다. 반면, readonly는 변수 자체와 변수에 할당된 객체 모두 변경할 수 없다.

  • 객체 리터럴 타입:
    const는 객체 리터럴 타입에 사용될 수 있다. readonly는 객체 리터럴 타입에 사용될 수 없지만, 속성이 읽기 전용일 경우 사용할 수 있다.

const obj = { name: "John" };
obj.name = "Mike"; // 유효한 할당
console.log(obj); // { name: "Mike" }

class Person {
  readonly name: string;
  constructor(name: string) {
    this.name = name;
  }
}

const person = new Person("John");
person.name = "Mike"; // 유효하지 않은 할당
console.log(person.name); // "John"

=> readonly 는 객체를 변경할 수 없다는 뜻이지, 변수에 재할당이 불가능하다는 의미가 아니다.

readonly는 얕게 동작한다

readonly가 얕게 동작한다는 것은, 객체나 배열의 내부 요소까지 불변성을 보장하지 않는다는 것을 의미한다.

interface Person {
  name: string;
  age: number;
}

const person: Readonly<Person> = {
  name: "John",
  age: 30,
};

person.name = "Mike"; // 에러: Cannot assign to 'name' because it is a read-only property.

const people: ReadonlyArray<Person> = [
  { name: "John", age: 30 },
  { name: "Jane", age: 25 },
];

people[0].name = "Mike"; // 에러: Cannot assign to 'name' because it is a read-only property.

위 코드에서, person 변수와 people 배열은 Readonly 타입으로 선언되어 있다. 따라서 이들은 변경될 수 없다.
그러나 person 변수의 경우, name과 age 속성은 모두 변경될 수 없는 반면 people 배열의 경우, 배열 자체는 변경할 수 없지만, 배열의 요소들은 변경될 수 있다. 이는 readonly가 얕게 동작한다는 것을 보여주는 예시이다.

따라서, ts에서 객체나 배열의 내부 요소의 불변성을 보장하려면, Deep Readonly 타입을 사용하거나, 직접 객체나 배열 내부의 요소들을 모두 readonly로 선언해야 한다.

🤑item 18 매핑된 타입을 사용하여 값을 동기화하기

매핑된 타입을 사용해서 관련된 값과 타입을 동기화하고, 인터페이스에 새로운 속성을 추가할 때 선택을 강제하도록 매핑된 타입을 고려해야 한다.
(💥매핑(Mapping)은 TypeScript에서 타입을 변환하는 과정 중 하나로, 하나의 타입에서 다른 타입으로 변환하는 것을 말한다. 매핑된 타입(Mapped Type)은 기존 타입에서 새로운 타입을 생성하는 것이며, { [P in K]: T }와 같은 형태로 사용된다.)

ts에서 매핑된 타입은 다른 타입을 기반으로 새로운 타입을 만드는 기능이다. 이를 활용하여 값을 동기화하는 것은 매우 유용한 기능 중 하나이다.

값을 동기화한다는 것은 여러 변수나 객체들의 값을 일치시키는 것을 의미하는데 이를 통해 값이 변경될 때마다 모든 변수나 객체를 일일이 수정하는 번거로움을 피할 수 있다.

예를 들어

interface Person {
  name: string;
  age: number;
}

let person: Person = {
  name: "John",
  age: 30,
};

let personCopy = {
  name: person.name,
  age: person.age,
};

위 코드에서 person 객체와 personCopy 객체는 동일한 값을 가지고 있다. 그러나 person 객체의 값을 변경하면, personCopy 객체의 값을 일일이 변경해주어야 한다.

이를 매핑된 타입을 사용하여 해결할 수 있는데 다음과 같이 SyncedPerson 타입을 정의한다.

type SyncedPerson<T> = {
  [K in keyof T]: T[K];
};

따라서, SyncedPerson 타입은 T 타입과 동일한 타입을 가지지만, 모든 속성들이 읽기 전용이며, 값을 변경할 수 없다.
( [K in keyof T]는 T 타입의 모든 속성들에 대해서 매핑된 타입을 생성하겠다는 의미
T[K]는 T 타입의 K 속성의 값을 의미)

이제, 위의 코드를 수정하면

interface Person {
  name: string;
  age: number;
}

type SyncedPerson<T> = {
  readonly [K in keyof T]: T[K];
};

let person: Person = {
  name: "John",
  age: 30,
};

let personCopy: SyncedPerson<Person> = person;

코드에서 personCopy 변수는 person 변수와 동일한 값을 가지지만, 모든 속성들이 읽기 전용으로 선언되어 있다. 따라서 person 객체의 값을 변경하면, personCopy 객체의 값도 자동으로 변경된다.

이를 통해 값의 동기화를 쉽게 구현할 수 있으며, 코드의 중복성과 오류를 줄일 수 있다.

-요약-

  • 매핑된 타입을 사용해서 관련된 값과 타입을 동기화하도록 하자.
  • 인터페이스에 새로운 속성을 추가할 때, 선택을 강제하도록 매핑된 타입을 고려해야 한다.

0개의 댓글

관련 채용 정보