준비물
property
자바스크립트의 class로 객체의 getter, setter 프로퍼티를 만들수 있다.
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
// Getter
get area() {
return this.calcArea();
}
}
spread syntax
자바스크립트에는 전개 구문, spread syntax가 있다. spread syntax로 객체를 쉽게 복제할 수 있다.
let objClone = { ...obj };
버그
컴파일이 잘 되는 간단한 타입스크립트 코드를 작성했다.
interface X {
v: number;
}
class Y implements X {
public get v() { return 1; }
}
const a: X = new Y();
const b: X = { ...a };
const c: X = { v: a.v };
console.log('a', a.v);
console.log('b', b.v);
console.log('c', c.v);
인터페이스 X와 이를 구현한 Y를 준비한다.
인터페이스 X에는 v
필드가 정의되어있다.
a
는 클래스에서 만들어진 객체이다.
b
는 a
에 spread syntax를 적용해서 만들어진 새로운 객체이다.
c
는 spread syntax 없이 X에 정의된 필드 각각을 손으로 연결해서 만든 객체이다.
a
, b
, c
는 인터페이스 X를 구현했으니까 v
로 접근할 수 있다.
예상되는 출력은 다음과 같다.
[LOG]: "a", 1
[LOG]: "b", 1
[LOG]: "c", 1
예상한 출력과 실제 결과가 같았으면 글을 쓰지 않았을거다. 실행 결과는 다음과 같다.
[LOG]: "a", 1
[LOG]: "b", undefined
[LOG]: "c", 1
b.v
가 undefined
가 되었다.
컴파일에는 문제가 없었지만 런타임에는 의도한 결과가 나오지 않는다.
더 자세하게 뜯어보기 위해 객체 자체도 로그로 찍어보자.
interface X {
v: number;
}
class Y implements X {
public get v() { return 1; }
}
const a: X = new Y();
const b: X = { ...a };
const c: X = { v: a.v };
console.log('a', a);
console.log('a.v', a.v);
console.log('b', b);
console.log('b.v', b.v);
console.log('c', c);
console.log('c.v', c.v);
[LOG]: "a", Y: {}
[LOG]: "a.v", 1
[LOG]: "b", {}
[LOG]: "b.v", undefined
[LOG]: "c", {
"v": 1
}
[LOG]: "c.v", 1
a
는 클래스에서 만들어진 객체라서 특별하다. property인 v
가 콘솔 로그에는 보이지 않지만 v
로 접근할 수 있다.
b
는 비어있는 자바스크립트 객체이다. spread syntax로 만들었기떄문에 클래스 Y와는 관계가 없다.
c
는 직접 만든 자바스크립트 객체이다. 원한대로 잘 돌아간다.
summary
타입스크립트 컴파일을 통과해도 런타임에서 터질 수 있다.
일반 자바스크립트 객체에만 spread syntax를 적용하자. 클래스로 만들어진 객체에와 spread syntax를 같이 쓰면 프로퍼티가 사고칠 수 있다.
spread syntax를 사용했을때는 의도와 실제 동작이 달라서 문제가 생겼다. 하지만 구식 방법으로 각각의 필드를 연결하면 직접 연결하면 원하는대로 돌아간다. 새로운 기술이라고 항상 좋은게 아니다. 엑셀 팡션을 사용하지 말라는 상사의 조언을 받아들이자.