준비물
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로 객체를 쉽게 복제할 수 있다.
버그
컴파일이 잘 되는 간단한 타입스크립트 코드를 작성했다.
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를 같이 쓰면 프로퍼티가 사고칠 수 있다.