설정 가져오기 기능이 작은 재귀 병합 함수를 써요. 매개변수 타입은 꼼꼼해 보이지만 프로그램이 기대하는 걸 적어 둔 것일 뿐이에요. 루프는 파싱된 파일에 실제로 있는 키를 그대로 돌아요.
type Settings = { theme: "light" | "dark"; fontSize: number };
function merge(target: any, src: any): void {
for (const key of Object.keys(src)) {
const v = src[key];
if (typeof v === "object" && v !== null) {
target[key] ??= {};
merge(target[key], v);
} else {
target[key] = v;
}
}
}
const saved: Partial<Settings> = JSON.parse('{"__proto__": {"isAdmin": true}}');
merge({}, saved);
console.log(({} as any).isAdmin); // true
JSON.parse는 __proto__를 평범한 자기 속성으로 정의해서 Object.keys가 그걸 돌려줘요. 그러면 일반 객체에서 target[key]는 상속된 접근자를 읽어서 Object.prototype을 얻고, ??=는 그게 nullish가 아니니 그냥 두고, 재귀 호출은 모든 객체가 공유하는 프로토타입에 isAdmin을 써요. JSON.parse가 any를 돌려주니 Partial<Settings> 표기는 아무 말 없이 통과했고요.
고민 중인 수정은 두 가지예요. 루프 안에서 위험한 키 이름을 건너뛰거나, 대상의 모든 단계를 Object.create(null)로 만들어서 부딪힐 접근자 자체를 없애거나요. 두 번째가 더 철저해 보이는데, 그러면 아래쪽 코드가 받는 객체마다 toString이 없어요. null 프로토타입 버전을 오래 유지해 본 분 있나요?