假设我知道某些部分数据的类型和嵌套数据结构,如下所示:
interface Element {
foo: string;
bar: string;
}
interface Elements {
[name: string]: Partial<Element>;
}
interface Scopes {
main: Elements;
[other: string]: Elements;
}
然后我有一个函数,它接受一些Scopes
类型的对象以及参数,该参数定义了我应该合并到输出对象的哪个范围(这将在没有范围的情况下展平)。该功能可能如下所示:
function merge<S extends Scopes>(obj: S, selectors?: { [K in keyof S]?: boolean }): UNKNOWNTYPE {
// this will be some logic to extract and merge scopes
}
我在为此函数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]
}
有没有办法从嵌套对象中动态展平和交叉第二级对象?
答案 0 :(得分:0)
好的,我喜欢这种事。首先:一般来说,这不可能做到这一点;你需要能够在类型级别枚举键,而TypeScript就不存在(现在或可能永远)。在某种程度上你可以做到这一点,你必须握住类型系统,因为它不可能自己推断出你想要它做什么。话虽如此,这是一个可能的前进方向:
按预期scopes
键的次数重载该函数的次数为:
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
}
然后,当您声明scopes
时,您可能不应该将类型声明为Scopes
,或者类型系统将忘记您设置的特定键。如果你只是让它推断一个类型,它将知道所有的键和值类型:
const scopes = {
main : {
a : {
foo: "a"
},
b : {
bar: "b"
}
},
other : {
b : {
foo : "b"
},
c : {
foo : "c"
}
}
}
最后,您已准备好致电merge()
。您必须明确指定泛型类型。第一个是scopes
变量的类型,其中包含您关注的所有键和值(仅typeof scopes
,因为我们没有将其设置为Scopes
),而下一个一个是selectors
中的各个键:
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
你得到的out
完全符合你想要的类型。所有这些箍跳都值得吗?随你(由你决定。祝你好运!