I would like to implement deep pick in typescript.
我想在打字稿中实施深度挑选。
My example code is:
我的示例代码是:
interface TestBook {
id: string;
name: string;
}
interface TestUser {
id: string;
email: string;
books: TestBook[];
}
I and I would like to use deep pick like:
我和我想使用深挑,如下所示:
const foo: DeepPick<TestUser, 'id' | 'books.name'> = {...
/*
{
id: ..
books: [{name: ...}]
}
*/
Problem: There is only Pick
in standard typescript and there is no library implement this DeepPick
.
问题:在标准打字稿中只有Pick,并且没有实现这个DeepPick的库。
How can I do it? Which technic should I use?
我该怎么做呢?我应该使用哪种技术?
I tried to find on google and SO.
我试着在谷歌上搜索。
更多回答
@caTS Looks like it's a whole other topic
@猫看起来像是另一个话题
Because its use unstandar way for array 'books.[].id'
but I need to use books.id
因为它对数组‘books.[].id’使用了不标准的方式,但我需要使用books.id
I came up with this mess. If it works for your use case, I can write up an answer.
我想出了这个烂摊子。如果它适合你的用例,我可以写一个答案。
Let's first define some utility types to get the "head" or "tail" of a path:
让我们首先定义一些实用程序类型来获取路径的“头”或“尾”:
type Head<T extends string> = T extends `${infer First}.${string}` ? First : T;
type Tail<T extends string> = T extends `${string}.${infer Rest}` ? Rest : never;
Then our DeepPick can take the heads of the paths and then deep pick the tail:
然后我们的DeepPick可以取下路径的头部,然后深挖尾巴:
type DeepPick<T, K extends string> = T extends object ? {
[P in Head<K> & keyof T]: T[P] extends readonly unknown[] ? DeepPick<T[P][number], Tail<Extract<K, `${P}.${string}`>>>[] : DeepPick<T[P], Tail<Extract<K, `${P}.${string}`>>>
} : T
If it's not an object, we shouldn't do anything to it. Inside the mapped type, I also added a case for arrays.
如果它不是一个物体,我们就不应该对它做任何事情。在映射类型中,我还为数组添加了一个大小写。
Playground
操场
I tried @zenly's version and found I was losing type hints, so I've modified it slightly. Here's a more opinionated version (zenly's handles a wider set of inputs) that persisted typehints better
我尝试了@zenly的版本,发现我丢失了类型提示,所以我对其进行了轻微的修改。这里有一个更固执己见的版本(zenly‘s处理更广泛的输入集),它更好地持久化了类型提示
type Head<T extends string> = T extends `${infer First}.${string}` ? First : T;
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
type Tail<T extends string> = T extends `${string}.${infer Rest}` ? Rest : never;
type DeepPick<TObject, TKey extends string> = UnionToIntersection<TObject extends object
? TKey extends `${infer A}.${infer B}`
? {
[P in Head<TKey> & keyof TObject]: DeepPick<TObject[P], Tail<Extract<TKey, `${P}.${string}`>>>;
}
: TKey extends keyof TObject
? Pick<TObject, TKey>
: never
: TObject>;
@zelsny answer is amazing, but it fails with arrays that can be null or undefined (T[] | null | undefined
). This is a solution I found:
@zelny的答案很棒,但对于可以为空或未定义的数组(T[]|NULL|未定义),它会失败。这是我找到的一个解决方案:
type DeepPick<T, K extends string> = T extends object
? NonNullable<T> extends readonly unknown[]
? DeepPick<NonNullable<T>[number], K>[] | Exclude<T, NonNullable<T>>
: {
[P in Head<K> & keyof T]: DeepPick<
T[P],
Tail<Extract<K, `${P}.${string}`>>
>
}
: T
Playground
操场
更多回答
Looks like there is a issue for property starting with same "key" (book vs books) tsplay.dev/mppOpm I think that infer
is problem
看起来属性有问题,以相同的“key”开头(Book Vs Book)tsplay.dev/mppOpm我认为推断是有问题的
@teteyi3241 I corrected the implementation now
@eteyi 3241我现在更正了实现
Wow, Is it magic ?
哇,这是魔术吗?
Is it possible to have type suggestions like normal Pick?
有没有可能有像Normal Pick这样的类型建议?
@BoomPT Have you tried the answer below?
@BoomPT你尝试过下面的答案吗?
我是一名优秀的程序员,十分优秀!