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

Javascript 如何提供动态生成的JSON的结构信息,javascript,typescript,npm,Javascript,Typescript,Npm,我想发布一个NPM包(用Typescript编写),如下所示: const networks={main:1,test:2,dev:3} 常量资源={ 傅:{ [networks.main]:“你好”, [networks.test]:“有”, [networks.dev]:“朋友” }, 酒吧:{ [networks.main]:“如何”, [networks.test]:“是”, [networks.dev]:“你” }, 巴兹:{ [networks.main]:“拥有一个”, [netw

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

const networks={main:1,test:2,dev:3}
常量资源={
傅:{
[networks.main]:“你好”,
[networks.test]:“有”,
[networks.dev]:“朋友”
},
酒吧:{
[networks.main]:“如何”,
[networks.test]:“是”,
[networks.dev]:“你”
},
巴兹:{
[networks.main]:“拥有一个”,
[networks.test]:“很好”,
[networks.dev]:“一天”
}
}
我想确保用户可以设置一次网络,而不必担心他们在哪个网络上:

const myMainResource=getResourceFor(networks.main)
const myTestResource=getResourceFor(networks.test)
const myDevResource=getResourceFor(networks.dev)
console.log(mymainsource.foo)/“你好”
console.log(myTestResource.foo)/“那里”
console.log(myDevResource.foo)/“friend”
这样,他们就不必写
resource.foo[networks.main]
,只需写
resource.foo
,就能得到同样的结果

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

我已经编写了
getResourceFor
函数,它通过递归所有资源并替换网络(这就是为什么在运行时“生成”数据)来正常工作

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

最好的方法是什么


如果你想看到真正的代码,它在这里:

我最终使用了以下答案:

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

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;
};
类型原语=
|串
|作用
|数
|布尔值
|象征
|未定义
|无效;
类型MappingToChangeFrom={
地址:{
[x:编号]:字符串;
};
};
类型MappingToChangeTo={
地址:字符串;
};
类型deepomit helper={
[P in keyof T]:T[P]扩展推断TP//触发同态行为所需的额外间接级别//分布在联合上
?TP扩展原语
?TP//不要使用原语和函数
:TP扩展任何[]
?阵列//阵列特殊处理
:TP扩展MappingToChangeFrom//如果类型等于{address:{[networkIds:number]:string}
?省略并映射到changeto//更改为{address:string}
:DeepOmit
:从不;
};
类型DeepOmit=T扩展原语?T:深度辅助;
类型数组={
[P in keyof T]:DeepOmit;
};
RawLegos类型=RawLegos类型;
输入RawLegosWithoutNetworkId=DeepOmit;
const isValidObject=(对象:未知)=>typeof对象==“对象”&&obj!==无效的
//递归地遍历每个字段,并将地址值更改为特定值
//即复合.cDai.address[mainnet]=0x。。。
//变成:
//composite.cDai.address=0x。。。。
导出常量changeAddressValue=(
networkId:number,
immutableObj:RawLegos,
):RawLegosWithoutNetworkId=>{
设obj=immutableObj,如有;
//递归基本情况,到此为止
如果(!isValidObject(immutableObj)){
返回obj;
}
//取消对象结构以创建新引用
obj={…不可变obj};
//在对象上使用for..in进行迭代
用于(obj中的常量键){
如果(Array.isArray(obj[key])继续;//忽略数组(例如ABI)
如果(!isValidObject(obj[key])继续;//忽略无效对象
如果(键==“地址”){
obj[key]=obj.address[networkId]| |空;
}否则{
obj[key]=changeAddressValue(networkId,obj[key]);
}
}
返回obj;
};

我最终使用了这个答案:

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

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;
};
类型原语=
|串
|作用
|数
|布尔值
|象征
|未定义
|无效;
类型MappingToChangeFrom={
地址:{
[x:编号]:字符串;
};
};
类型MappingToChangeTo={
地址:字符串;
};
类型deepomit helper={
[P in keyof T]:T[P]扩展推断TP//触发同态行为所需的额外间接级别//分布在联合上
?TP扩展原语
?TP//不要使用原语和函数
:TP扩展任何[]
?阵列//阵列特殊处理
:TP扩展MappingToChangeFrom//如果类型等于{address:{[networkIds:number]:string}
?省略并映射到changeto//更改为{address:string}
:DeepOmit
:从不;
};
类型DeepOmit=T扩展原语?T:深度辅助;
类型数组={
[P in keyof T]:DeepOmit;
};
RawLegos类型=RawLegos类型;
输入RawLegosWithoutNetworkId=DeepOmit;
const isValidObject=(对象:未知)=>typeof对象==“对象”&&obj!==无效的
//递归地遍历每个字段,并将地址值更改为特定值
//即复合.cDai.address[mainnet]=0x。。。
//变成:
//composite.cDai.address=0x。。。。
导出常量changeAddressValue=(
networkId:number,
immutableObj:RawLegos,
):RawLegosWithoutNetworkId=>{
设obj=immutableObj,如有;
//递归基本情况,到此为止
如果(!isValidObject(immutableObj)){
返回obj;
}
//取消对象结构以创建新引用
obj={…不可变obj};
//在对象上使用for..in进行迭代
用于(obj中的常量键){
如果(Array.isArray(obj[key])继续;//忽略数组(例如ABI)
如果(!isValidObject(obj[key])继续;//忽略无效对象
如果(键==“地址”){
obj[key]=obj.address[networkId]| |空;
}否则{
obj[key]=changeAddressValue(networkId,obj[key]);
}
}
返回obj;
};
“因为对象是在运行时动态创建的”-y中的对象