我正在尝试创建一个函数,以简化可用于任意目的的对象上的set
ter函数的设置。明显的用途包括类型防护,值和边界检查或触发事件。
这个想法是要获取一个形状已知的对象,并对其调用一个函数,该函数将返回一个具有相同可枚举形状的新对象,其中该对象的每个成员实际上都具有set
和{{1} }“属性”。 get
ter来自函数的第二个参数,而set
ter仅返回“ protected”值。
此方法的大量实用程序来自可能的严格键入。当受保护的对象是在代码的不同部分中定义的,甚至可能超出您的控制范围时,此功能特别有用。如果对象形状发生变化,则键入错误将确保添加/删除新的get
ter。
我很容易地创建了一个“扁平”版本。
平面版本
set
用法
function makeObjectSetter<T extends {}>(
internal: T,setters: {
[P in keyof T]: (next: T[P]) => void;
}
) {
const ret = {};
for (const x in internal) {
Object.defineProperty(ret,x,{
set: setters[x],get: () => internal[x],enumerable: true,});
}
return ret as T;
}
当目标对象具有嵌套对象时,就会出现棘手的部分。虽然可以使用平面版本,但是编写更深的嵌套变得很麻烦。
因此,我正在尝试编写平面函数的递归版本。这将检测成员的类型是否是对象,并对其进行不同的处理。我相信我的函数签名类型正确,但是函数内部存在一个硬错误,我无法弄清。
我知道TypeScript类型在运行时不可用,但是我认为这是一个编译时问题。我相信,如果在上述行中得到正确的语法,它将可以正常工作。但是也许我错了?
递归版本
const myobject = {
num: 42,str: 'initialValue',};
const protectedObject = makeObjectSetter(myobject,{
num(x) {
// Make sure positive
myobject.num = Math.max(x,0);
},str(s) {
// Always double the input
myobject.str = s + s;
},});
console.log(myobject);
// { num: 42,str: 'initialValue' }
protectedObject.num = -1;
protectedObject.str = 'a';
console.log(myobject);
// { num: 0,str: 'aa' }
for (let x in protectedObject) console.log(x);
// num
// str
在type nestedSetters<T extends {}> = { [P in keyof T]: T[P] extends {} ? nestedSetters<T[P]> : (next: T[P]) => void };
function makeObjectSetterRecursive<T extends {}>(internal: T,setters: nestedSetters<T>) {
const ret = {};
for (const x in internal) {
let prop: PropertyDescriptor;
// Can't figure out this line
type t = typeof internal[x];
// Pretty sure this test is the right runtime test for my purposes
if (typeof internal[x] == 'object') {
prop = {
value: makeObjectSetterRecursive(internal[x],setters[x] as nestedSetters<t>),// Should be able to avoid this `as` cast,no?
};
} else {
prop = {
set: setters[x] as (next: t) => void,no?
get: () => internal[x],};
}
prop.enumerable = true;
Object.defineProperty(ret,prop);
}
return ret as T; // Extra extra bonus get rid of this `as` cast
}
旁边,我也尝试过typeof internal[x]
和其他猜测,但无济于事。
任何对此的想法将不胜感激。答案可能是我想要的是不可能的。
附带要求:我认为Pick<typeof internal,x>
强制类型转换不需要带有正确的类型提示。