이번 글은 스킬이기보다는 타입스크립트 작성 시에 추천 사항이다.
readonly를 사용하자
타입스크립트로 프로젝트를 작성하더라도 readonly는 한 번도 작성을 안 하는 경우가 많다. 이유인즉슨 꼭 써야 되는 상황이 있는 것이 아니기 때문이다. 그렇다면 왜 쓰자고 권장하는 것일까. readonly는 Mutation(수정)으로 부터 발생할 수 있는 에러를 막아주기 때문이다. 예제를 통해 살펴보자.
Trianular numbers 예제
Trianular number(=triangle number)는 삼각형 Dot Pattern을 만들 수 있는 숫자를 의미한다.
이제 수학 공부도 할겸 이거를 계산하는 것을 typescript를 이용해서 코드로 짰다고 가정해 보자.
function arraySum(arr: number[]) {
let sum = 0;
let num;
while ((num = arr.pop()) !== undefined) {
sum += num;
}
return sum;
}
function trianularNumber(n: number) {
const nums = [];
for (let i = 0; i <= n; i++) {
nums.push(i);
}
return arraySum(nums);
}
console.log(trianularNumber(5)); // 15
console.log(trianularNumber(3)); // 6
console.log(trianularNumber(4)); // 10
console.log(trianularNumber(1)); // 1
결괏값이 너무 잘 나와서 만족했다.
그런데 문제의 요구사항이 바뀌어서 0~n까지의 trianularNumber를 콘솔로 보여달라고 했다. 결괏값도 잘 나왔으니 "for문에 콘솔만 찍으면 되겠지?" 하고 trianularNumber()의 for문 안에서 arraySum()함수의 콘솔을 찍었다.
function trianularNumber(n: number) {
const nums = [];
for (let i = 0; i <= n; i++) {
nums.push(i);
console.log(arraySum(nums)); // 바뀐 부분
}
}
trianularNumber(4);
콘솔에는 어떻게 나왔을까?
뭐지하고 코드를 다시 봤다면 문제점을 발견할 수 있을 것이다.
function arraySum(arr: number[]) {
let sum = 0;
let num;
while ((num = arr.pop()) !== undefined) {
sum += num;
}
return sum;
}
계속 배열을 값을 비우는 side effect를 만들고 있었던 것이다. 이것을 해결할 수 있는 가장 좋은 방법은 arraySum() 함수에서 배열을 수정하지 못하게 하는 것이다. 그것을 타임스크립트에서는 readonly가 도와준다.
number[] 타입 선언 앞에 readonly 키워드를 붙여주었더니, 바로 에러 표시가 나고 있다.
'readonly number[]' 형식에 'pop' 속성이 없습니다.
readonly 키워드를 붙여주면 해당 엘리먼트는 아래와 같은 특징이 생긴다.
- 읽을 수는 있지만 쓸 수는 없다.
- length를 읽을 수는 있지만, 프로퍼티를 추가할 수 없다.
- mutate를 할 수 있는 모든 메서드들을 사용할 수 없다.
에러 표시 덕분에 어디가 잘못되었는지 파악했고 로직을 바로 수정할 수 있다.
function arraySum(arr: readonly number[]) {
let sum = 0;
for (const num of arr) {
sum += num;
}
return sum;
}
function trianularNumber(n: number) {
const nums = [];
for (let i = 0; i <= n; i++) {
nums.push(i);
console.log(arraySum(nums)); // 바뀐 부분
}
}
trianularNumber(4);
다시 실행해 보면 내가 원하는 결과가 나왔음을 확인할 수 있다. 여기서 중요한 점은 내가 프로퍼티를 수정할 마음이 없다면 가능한 readonly를 사용하는 것이 좋다는 것이다. 결과가 잘 나오기 때문에 그냥 넘어갔다면 저 side effect는 훗날 어떤 에러를 발생시킬지 모르고 저 위치에서 발생했다는 것도 찾기 힘들 것이다.
참고
- readonly는 array와 tuple literal에만 적용 가능하다. object에는 Readonly<>라는 동일한 기능을 하는 제네릭을 사용해 주면 된다.
- readonly는 shallow하다.
type Person = {
name: string;
address: {
address1: string;
address2: string;
};
};
const person: Readonly<Person> = {
name: "jacob",
address: {
address1: "korea",
address2: "seoul"
}
};
// Cannot assign to 'address' because it is a read-only property.ts(2540)
person.address = { address1: "korea", address2: "Busan" };
person.address.address2 = "Busan";
console.log(person.address.address2); // Busan
'Typescript' 카테고리의 다른 글
타입스크립트를 도입하며, (0) | 2021.08.07 |
---|---|
enum 대신 object를 generic을 이용하여 union type으로 변경하기 (1) | 2021.03.20 |
더 나은 타입스크립트 작성하기 2 - Skills (0) | 2020.01.01 |
타입스크립트 - 배열의 값을 타입(union)으로 바꾸기(const assertions) (0) | 2019.12.31 |
더 나은 타입스크립트 작성하기 1 - Skills (0) | 2019.12.27 |