Typescript 按接口深度拾取

Typescript 按接口深度拾取,typescript,typescript-typings,Typescript,Typescript Typings,假设我们有一个目标 让obj={ 福:‘福’, 酒吧:“酒吧”, 信件:{ a:‘一些a’, b:‘一些b’, } } 我们想根据定义进行深入挑选: let投影={ 傅:1,, 信件:{ b:1 } } 它将产生: 让结果={ 福:‘福’, 信件:{ b:一些b' } } 问题不在于如何在javascript中实现这一点,而是非常简单。但是TypeScript定义是什么样子的 类型t项目={ [key in U]?:T[key]扩展对象?T项目:编号; } 函数选取( obj:T, 投影:

假设我们有一个目标

让obj={
福:‘福’,
酒吧:“酒吧”,
信件:{
a:‘一些a’,
b:‘一些b’,
}
}
我们想根据定义进行深入挑选:

let投影={
傅:1,,
信件:{
b:1
}
}
它将产生:

让结果={
福:‘福’,
信件:{
b:一些b'
}
}
问题不在于如何在javascript中实现这一点,而是非常简单。但是TypeScript定义是什么样子的

类型t项目={
[key in U]?:T[key]扩展对象?T项目:编号;
}
函数选取(
obj:T,
投影:t投影
):选择{
返回null;
}

这只会导致顶级pick,
字母
字段包括
a
b
函数的实现可能有点重要,因为我在这里提出的键入中可能存在需要测试的边缘情况。希望您可以将此作为起点并进行调整以获得边缘案例

首先,有必要描述
投影
,这是
投影的可接受类型
对于
T
类型的
obj
值:

type Projection<T extends object> = {
  [K in keyof T]?: T[K] extends object ? Projection<T[K]> : number
};
基本上,我们正在浏览
T
p
的常用键。如果
P
中的属性是一个
数字
,我们将从
T
输出该属性。如果它是一个
对象
,那么我们递归到
T[K]
P[K]
并重复。唯一看起来奇怪的部分是结尾的
extensedinfero..
位。这是一个迫使编译器急切地评估输出类型而不是延迟它的技巧。如果您想要像
{foo:string;letters:{b:string}}
这样的输出类型,而不是
DeepPickByProjection
,那么您需要这样的技巧。有关其工作原理的详细信息,请参阅

好的,让我们看看打字是否有效:

function pick<T extends object, P extends Projection<T>>(
  obj: T,
  projection: P
): DeepPickByProjection<T, P> {
  return null!;
}

const result = pick(obj, projection);
/* const result: {
    foo: string;
    letters: {
        b: string;
    };
} */
函数选取(
obj:T,
投影:P
):DeepPickByProjection{
返回null!;
}
常量结果=拾取(对象、投影);
/*常数结果:{
foo:string;
信件:{
b:弦;
};
} */
看起来不错。
result
的输出类型与投影过滤的
obj
的输入类型相同。万岁

记住,虽然可能有很多边缘情况。对于初学者,我会关心可选属性、联合属性和数组值属性,并确保有人不希望传入类似
{letters:1}
的内容,意思是“在此处复制整个子树”,因为我认为上面的键入禁止这样做。正如我所说的,这应该被认为是一个启动点,而不是您的用例的一个生产就绪的插件


太棒了,非常感谢你的帮助。我添加了
(TProjection | number)
以允许
{letters:1}
和数组支持,现在我们所有的mongodb投影都是类型安全的-请不要编辑您的问题以包含答案-这只会让读者对问题的含义感到困惑。人们已经可以在下面找到答案。我已经回滚了您的编辑。@kaya3但是我的编辑没有复制过去,我已经为各种情况添加了额外的支持-这些对于发现下面的答案的人来说绝对有用,因为下面的答案有一些限制。然后将您自己的答案作为答案发布。这不是问题的一部分。
function pick<T extends object, P extends Projection<T>>(
  obj: T,
  projection: P
): DeepPickByProjection<T, P> {
  return null!;
}

const result = pick(obj, projection);
/* const result: {
    foo: string;
    letters: {
        b: string;
    };
} */