Symbol은 JS의 원시타입 중 하나로
고유함을 보장하는 값을 나타낸다.
const a = Symbol("a")
const b = Symbol("a")
console.log(a === b) // false
Symbol로 같은 키값으로 생성한다고 해도 늘 고유한 값을 생성하기 때문에
위 비교에서 false가 나온다.
const a = Symbol.for("a")
const b = Symbol.for("a")
console.log(a === b) // true
symbol.for는 전역 symbol 레지스트리에 저장이된다.
js 런타임 내의 전역을 말한다.
전역에 없으면 새로 생성하고
있으면 기존의 symbol을 호출한다.
그래서 위 비교에서 true이다.
const a = Symbol("a")
const b = Symbol.for("a")
console.log(a === b) // false
Symbol과 Symbol.for 둘다 Symbol을 생성하니깐
위 코드가 서로 같다고 생각 할 수 있지만
Symbol.for는 Symbol.for로 생성된 Symbol만 비교가 가능하기 때문에
false
가 나온다.
unique symbol
은 타입스크립트 2.7에서 소개된 타입이다.
unique symbol 타입은 symbol
의 서브타입으로
Symbol 또는 Symbol.for로 생성되는 경우 또는 직접적으로 타입 명시된 경우에만 선언된다.
또한 const
에만 명시 가능하다.
위에서 symbol.for의 경우 기존에 존재하면 그것을 가져온다고 했다.
const b: unique symbol = Symbol.for("a");
const c: unique symbol = Symbol.for("a");
console.log( b === c );
JS 런타임에서는 같은 전역 심볼을 가르켜 문제가 없지만
컴파일 타임에서는 unique symbol
타입을 갖는 경우에는 서로 같은 symbol을 갖는다고 해도
고유한 symbol로 보기 때문에 에러 밑줄을 보게 된다.
타입 시스템 차원에서 심볼이 고유하다는 것을 나타내주어
조금 더 안전하고 예측가능한 코드 작성에 도움을 준다.
const MESSAGE_TYPE = Symbol();
// const MESSAGE_TYPE: unique symbol = Symbol(); // 타입 생략가능.
const Test= Symbol();
interface Message {
type: typeof MESSAGE_TYPE;
payload: string;
}
let message: Message = {
type: MESSAGE_TYPE,
payload: "Hello, world!",
};
let message2: Message = {
type: TEST, // 컴파일 타임 에러.
payload: "Hello, world!",
};
console.log(message.type === MESSAGE_TYPE); // true
console.log(message.type === TEST); // 컴파일 타임 에러.
위와 같은 경우 의도치 않는 속성의 접근이나 수정을 컴파일 타임에서 방지할 수 있다.
Nest code를 살펴보면
symbol과 symbol.for을 둘 다 아래와 같은 형태로 사용 중이다.
export const aa = Symbol("test");
export const bb = Symbol.for("test1");
Symbol의 경우 export const SYMBOL_TOKEN = Symbol('token');
와 같이 같은 key 이름으로 여러 모듈에서 선언되어 있고
같은 디렉토리의 다른 모듈에서 호출해서 사용한다.
https://github.com/nestjs/nest/blob/master/integration/inspector/src/properties/properties.service.ts#L4
https://github.com/nestjs/nest/blob/master/integration/injector/src/properties/properties.service.ts#L4
Symbol.for의 경우 export
를 사용해 선언이 되지만 선언된 모듈에서만 사용된다.
https://github.com/nestjs/nest/blob/master/packages/core/helpers/handler-metadata-storage.ts#L9
https://github.com/nestjs/nest/blob/master/packages/core/injector/instance-wrapper.ts#L21...L22
require.cache를 사용하여 모듈의 캐쉬를 지우게 되면
Symbol의 경우 모듈이 리로드 되면서 기존의 Symbol과 다른 Symbol이 생성이 된다.
Symbol.for의 경우는 전역 레지스트리에 저장되어 있기 때문에 캐쉬를 초기화 해도 똑같다.
require.cache
를 호출해서 삭제하는 경우는 별로 없겠지만
symbol 사용할 때는 조심하자.
Symbol는 같은 키로 선언되어도 유니크한데 같은 변수이름으로 선언해서 사용하면
import 해서 사용할 때 헷갈리지 않나?
Symbol.for의 경우 굳이 export 하지 않아도 다른 모듈에서 호출해서 사용 할수 있기도 하고
export 했으면 다른 모듈에서 사용되야 할텐데 그렇지 않다. 왜?