如何提供关于动态生成的JSON的结构信息



我想发布一个NPM包(用Typescript编写(,如下所示:

const networks = { main: 1, test: 2, dev: 3 }
const resource = {
foo: {
[networks.main]: "hello",
[networks.test]: "there",
[networks.dev]: "friend"
},
bar: {
[networks.main]: "how",
[networks.test]: "are",
[networks.dev]: "you"
},
baz: {
[networks.main]: "have a",
[networks.test]: "good",
[networks.dev]: "day"
}
}

我想确保用户可以设置一次网络,而不必担心他们在哪个网络上:

const myMainResource = getResourceFor(networks.main)
const myTestResource = getResourceFor(networks.test)
const myDevResource = getResourceFor(networks.dev)
console.log(myMainResource.foo) // "hello"
console.log(myTestResource.foo) // "there"
console.log(myDevResource.foo) // "friend"

这样,不必写resource.foo[networks.main],他们只需写resource.foo就可以得到同样的东西。

实际对象实际上嵌套得更多,例如:alice.contracts.foobob.protocol.bar等。但数据的存储方式是,网络始终位于树的叶节点。

我已经编写了getResourceFor函数,它通过在所有资源中递归并替换网络(这就是为什么数据在运行时"生成"的原因(,可以按预期工作。

但我也想在编辑器中自动完成工作(使用VS代码(。由于对象是在运行时动态创建的,编辑器无法真正知道生成的JSON对象的形状。

解决这一问题的最佳方法是什么?

如果您想查看真实的代码,请点击此处:https://github.com/studydefi/money-legos

我最终使用了这个答案:使用类型脚本的深度省略

它超级乱,但嘿,至少它有效。结果如下:

type Primitive =
| string
| Function
| number
| boolean
| Symbol
| undefined
| null;
type MappingToChangeFrom = {
address: {
[x: number]: string;
};
};
type MappingToChangeTo = {
address: string;
};
type DeepOmitHelper<T> = {
[P in keyof T]: T[P] extends infer TP //extra level of indirection needed to trigger homomorhic behavior // distribute over unions
? TP extends Primitive
? TP // leave primitives and functions alone
: TP extends any[]
? DeepOmitArray<TP> // Array special handling
: TP extends MappingToChangeFrom // IF type equals to { address: { [networkIds: number]: string } }
? Omit<TP, "address"> & MappingToChangeTo // Change to { address: string }
: DeepOmit<TP>
: never;
};
type DeepOmit<T> = T extends Primitive ? T : DeepOmitHelper<T>;
type DeepOmitArray<T extends any[]> = {
[P in keyof T]: DeepOmit<T[P]>;
};
type RawLegos = typeof rawLegos;
type RawLegosWithoutNetworkId = DeepOmit<RawLegos>;
const isValidObject = (obj: unknown) => typeof obj === "object" && obj !== null;
// Recursively goes through each field, and changes the address value to the specific value
// i.e. compound.cDai.address[mainnet] = 0x...
//      becomes:
//      compound.cDai.address = 0x....
export const changeAddressValue = (
networkId: number,
immutableObj: RawLegos,
): RawLegosWithoutNetworkId => {
let obj = immutableObj as any;
// recursive base case, stop here
if (!isValidObject(immutableObj)) {
return obj;
}
// desctructure the object to create new reference
obj = { ...immutableObj };
// iterating over the object using for..in
for (const key in obj) {
if (Array.isArray(obj[key])) continue; // ignore arrays (e.g. ABIs)
if (!isValidObject(obj[key])) continue; // ignore non-valid objects
if (key === "address") {
obj[key] = obj.address[networkId] || null;
} else {
obj[key] = changeAddressValue(networkId, obj[key]);
}
}
return obj;
};

最新更新