Typescript 用于提取嵌套对象键的类型

Typescript 用于提取嵌套对象键的类型,typescript,types,Typescript,Types,假设我知道某部分数据和嵌套数据结构的类型,如下所示: interface Element { foo: string; bar: string; } interface Elements { [name: string]: Partial<Element>; } interface Scopes { main: Elements; [other: string]: Elements; } 我无法正确定义此函数的输出类型UNKNOWNTYP

假设我知道某部分数据和嵌套数据结构的类型,如下所示:

interface Element {
    foo: string;
    bar: string;
}

interface Elements {
    [name: string]: Partial<Element>;
}

interface Scopes {
    main: Elements;
    [other: string]: Elements;
}
我无法正确定义此函数的输出类型
UNKNOWNTYPE
。我想动态展开并从输入对象提取关键点,因此对于以下输入数据,我将获得以下输出:

const scopes: Scopes = {
  main : {
    a : {
       foo: "a"
    },
    b : {
       bar: "b"
    }
  },
  other : {
    b : {
       foo : "b"
    },
    c : {
       foo : "c"
    }
  }
}

const out = merge(scopes, { main : true, other : true });

/*
out is of this strcture

{
  a : {
    foo : "a"
  },
  b : {
    foo : "b"
    bar : "b"
  },
  c : {
    foo : "c"
  }
}
*/
我尝试了一些方法并尝试了这种类型,但它无法解析所有合并作用域的键:

interface UNKNOWNTYPE<Scopes, Scope extends keyof Scopes = keyof Scopes> {
    [K in keyof Scopes[Scope]: Element]
}
接口未知类型{
[K in keyof Scopes[Scope]:元素]
}

有没有办法从嵌套对象动态展平和相交二级对象?

好的,我喜欢这种东西。首先:一般来说,不可能做到这一点;您需要能够在类型级别枚举键,而TypeScript根本不存在(现在或者将来)。在一定程度上,您必须握住类型系统的手,因为它不太可能推断出您希望它自己做什么。也就是说,有一个可能的前进方向:

按照您预期的
作用域
的键数,多次重载函数:

function merge<S extends Scopes, KA extends keyof S>(obj: S, selectors?: {[K in KA]?: boolean}): S[KA];
function merge<S extends Scopes, KA extends keyof S, KB extends keyof S>(obj: S, selectors?: {[K in KA | KB]?: boolean}): S[KA] & S[KB];
function merge<S extends Scopes, KA extends keyof S, KB extends keyof S, KC extends keyof S>(obj: S, selectors?: {[K in KA | KB | KC]?: boolean}): S[KA] & S[KB] & S[KC];
//... merge with 4, 5, 6, etc
function merge<S extends Scopes>(obj: S, selectors?: { [K in keyof S]?: boolean }): Elements {
   // this will be some logic to extract and merge scopes
  return null as any; // for now
}
最后,您可以调用
merge()
。必须显式指定泛型类型。第一个是
作用域
变量的类型,包含您关心的所有键和值(这只是
作用域类型
,因为我们没有将其设置为
作用域
),下一个是
选择器中的各个键

const out = merge<typeof scopes, 'main','other'>(scopes, { main: true, other: true });
out.a.foo; 
out.a.bar; // error as expected
out.b.foo;
out.b.bar;
out.c.foo;
out.c.bar; // error as expected
const out=merge(作用域,{main:true,other:true});
out.a.foo;
户外酒吧;//错误如预期
out.b.foo;
户外酒吧;
out.c.foo;
室外钢筋;//错误如预期

然后你会得到一个你想要的类型的
。这值得跳这么多篮球吗?这取决于你。祝你好运

你能试着用更简单的术语解释一下你想做什么吗?这仅仅是提取二级对象并组合它们的问题吗?谢谢@jcalz的解释。我提出了类似的解决方案,但事实并非如此。我知道的唯一一件事是,
main
property将始终被定义,并且任何其他属性(它们的名称将是未知的)也可以包含您想要的任意多个附加“作用域”,因此编写许多重载似乎是一种过火的行为。似乎打字机可以;不幸的是,目前还不能动态地处理这个案件。也许我只是采用类似于scala的方法,然后定义22个(元组的任意数)重载并返回
元素

const scopes = {
  main : {
    a : {
       foo: "a"
    },
    b : {
       bar: "b"
    }
  },
  other : {
    b : {
       foo : "b"
    },
    c : {
       foo : "c"
    }
  }
}
const out = merge<typeof scopes, 'main','other'>(scopes, { main: true, other: true });
out.a.foo; 
out.a.bar; // error as expected
out.b.foo;
out.b.bar;
out.c.foo;
out.c.bar; // error as expected