英文:
Typescript: generate generic keys recursively, concatenating keys and ignoring objects
问题
The lib Prisma accepts filters like this:
Prisma库接受这样的过滤器:
By giving the object:
通过提供对象:
I want to generate the type:
我想生成这种类型:
I managed to get this:
我成功得到了这个:
That gives me:
这给了我:
英文:
The lib Prisma accepts filters like this:
prisma.user.findMany({
where: {
name: { contains: 'jeff' },
city: {
name: { contains: 'hol' },
state: {
acr: {
equals: 'vegas'
}
}
}
}
})
By giving the object:
{
name: '',
city: {
name: '',
state: {
acr: ''
}
}
}
I want to generate the type:
type UserFilters = {
name_contains?: string,
name_equals?: string,
city_name_contains?: string
city_name_equals?: string
city_state_acr_contains?: string
city_state_acr_equals?: string
}
I managed to get this:
type FilterR<Form> = Form extends object
? Partial<{
[Key in string & keyof Form as `${Key}_${'contains' | 'equals'}`]: FilterR<Form[Key]>
}>
: Form
That gives me:
Partial<{
name_contains: "";
name_equals: "";
city_contains: Partial<{
name_contains: "";
name_equals: "";
state_contains: Partial<{
acr_contains: "";
acr_equals: "";
}>;
state_equals: Partial<{
acr_contains: "";
acr_equals: "";
}>;
}>;
city_equals: Partial<...>;
}>
答案1
得分: 1
Sure, here is the translated content:
让我们创建一个类型,它将返回具有以下形状的类型:
Record<originalKey, updatedKey>
updatedKey
将取决于原始键下的属性是否是对象。如果它是一个对象,那么我们将在更新的键的开头添加原始键和 _
,然后对属性进行递归调用,否则,我们将在原始字符串的末尾添加 'contains' | 'equals'
。从这个形状中,我们可以看出我们需要所有值的联合,可以使用以下类型来实现:
type ValueOf<T> = T[keyof T];
让我们为过滤器创建一个类型:
type Filter = 'contains' | 'equals';
用于获取键的类型:
type FilterKeys<T> = ValueOf<{
[K in keyof T & string]: T[K] extends object
? `${K}_${FilterKeys<T[K]>}`
: `${K}_${Filter}`;
}>;
测试:
// "name_contains" | "name_equals" | "city_name_contains" | "city_name_equals" | "city_state_acr_contains" | "city_state_acr_equals"
type A = FilterKeys<typeof obj>;
看起来不错!
剩下的就是在 映射类型 中使用这些键了:
type FilterR<T> = { [K in FilterKeys<T>]?: string };
// type A = {
// name_contains?: string | undefined;
// name_equals?: string | undefined;
// city_name_contains?: string | undefined;
// city_name_equals?: string | undefined;
// city_state_acr_contains?: string | undefined;
// city_state_acr_equals?: string | undefined;
// }
type A = FilterR<typeof obj>;
英文:
Let's create a type that will return a type with the following shape:
Record<originalKey, updatedKey>
updatedKey
will depend on whether the property under the original key is an object or not. If it is an object then we prepend the original key to the beginning of the updated key with _
and call the type recursively for the property, otherwise, we will add 'contains' | 'equals'
to the end of the original string. From the shape, we can tell that we need a union of all values, which can be done with the following type:
type ValueOf<T> = T[keyof T];
Let's create a type for the filter:
type Filter = 'contains' | 'equals';
Type for getting keys:
type FilterKeys<T> = ValueOf<{
[K in keyof T & string]: T[K] extends object
? `${K}_${FilterKeys<T[K]>}`
: `${K}_${Filter}`;
}>;
Testing:
// "name_contains" | "name_equals" | "city_name_contains" | "city_name_equals" | "city_state_acr_contains" | "city_state_acr_equals"
type A = FilterKeys<typeof obj>;
Looks good!
What's left is just to use these keys in a mapped type:
type FilterR<T> = { [K in FilterKeys<T>]?: string };
// type A = {
// name_contains?: string | undefined;
// name_equals?: string | undefined;
// city_name_contains?: string | undefined;
// city_name_equals?: string | undefined;
// city_state_acr_contains?: string | undefined;
// city_state_acr_equals?: string | undefined;
// }
type A = FilterR<typeof obj>;
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论