给定数据库密钥成员,从实体中提取数据库密钥类型
Extract the db key type from an entity, given the db key members
给定一个实体:
const someEntity = {
id: 'someId',
createdAt: 123234223453,
name: 'Dudemanson',
}
它是 db 关键成员:
const keyMembers = {
hash: 'id',
range: 'createdAt',
}
我想提取数据库密钥:
const extractedDbKey = {
id: 'someId',
createdAt: 123234223453,
}
这是我失败的尝试:
type KeyFields<Entity extends object> = { hash: keyof Entity, range?: keyof Entity };
function toKey<Entity extends object, TheKeyFields extends KeyFields<Entity>>
(entity: Entity, keyFields: TheKeyFields): Pick<Entity, TheKeyFields[keyof TheKeyFields]> {
const ret = {} as Pick<Entity, TheKeyFields[keyof TheKeyFields]>;
(Object.keys(keyFields) as Array<keyof TheKeyFields>)
.map((k) => keyFields[k]).forEach(k => ret[k] = entity[k]);
return ret;
}
请指点一下?
如果您只对根据输入类型计算输出类型感兴趣,您将最终使用 Pick
。首先让我们定义输入类型:
const someEntity = {
id: 'someId',
createdAt: 123234223453,
name: 'Dudemanson',
}
type SomeEntityType = typeof someEntity;
const keyMembers = {
hash: 'id',
range: 'createdAt',
} as const; // <-- need that, or values will just be string
type KeyMembersType = typeof keyMembers;
请注意,您需要类似 const
assertion to have the compiler remember that the property values of keyMembers
are of string literal types 的东西,例如 "id"
和 "createdAt"
,而不是无用的 string
。
并且输出类型最终在对象类型 SomeEntityType
上使用 Pick
,键来自 属性 values 24=],像这样:
type ExtractedDbKeyType =
Pick<SomeEntityType, KeyMembersType[keyof KeyMembersType]>;
/* type ExtractedDbKeyType = {
id: string;
createdAt: number;
}*/
如果你想写一个像这样工作的函数签名,你可以这样做:
function extractKeys<T extends object, K extends Record<keyof K, keyof T>>(
entity: T,
keys: K
) {
const ret = {} as Pick<T, K[keyof K]>;
(Object.keys(keys) as Array<keyof K>).
map((k) => keys[k]).forEach(k => ret[k] = entity[k]);
// Without the following assertion you get an output type like
// 'Pick<{...},"..."|"..."">' instead of a more straightforward object type.
return ret as any as (typeof ret extends infer O ? { [P in keyof O]: O[P] } : never);
}
实施只是一个例子;重要的是它采用 T
和 K
类型的值(其中 K
受到限制,因此其值来自 keyof T
)和 return 的值输入 Pick<T, K[keyof K]>
。 return 类型有点问题;如果你只是 return Pick<T, K[keyof K]>
,它往往会显示为 Pick<BlahBlah, "x"|"y"|"z">
(至少在我的 IDE 中是这样)。所以最后我使用映射条件类型将对象类型扩展为更直接的对象类型。像这样:
const extractedDbKey = extractKeys(someEntity, keyMembers);
/* TYPE WITH ASSERTION:
const extractedDbKey: {
id: string;
createdAt: number;
} */
/* TYPE WITHOUT ASSERTION:
const extractedDbKey: Pick<{
id: string;
createdAt: number;
name: string;
}, "id" | "createdAt"> */
可以看出前者更好看。不过这是否重要取决于您。
哦,至少在你的例子中实现是合理的:
console.log(extractedDbKey) // {id: "someId", createdAt: 123234223453}
好的,希望对您有所帮助;祝你好运!
编辑:如果您特别想强制执行 keyMembers
具有属性 hash
和 range
,您可以修改 extractKeys()
:
function extractKeys<T extends object, K extends keyof T>(
entity: T,
keys: { hash: K, range: K }
) {
const ret = {
[keys.hash]: entity[keys.hash],
[keys.range]: entity[keys.range]
} as Pick<T, K>;
return ret as any as (typeof ret extends infer O ? { [P in keyof O]: O[P] } : never);
}
干杯!
给定一个实体:
const someEntity = {
id: 'someId',
createdAt: 123234223453,
name: 'Dudemanson',
}
它是 db 关键成员:
const keyMembers = {
hash: 'id',
range: 'createdAt',
}
我想提取数据库密钥:
const extractedDbKey = {
id: 'someId',
createdAt: 123234223453,
}
这是我失败的尝试:
type KeyFields<Entity extends object> = { hash: keyof Entity, range?: keyof Entity };
function toKey<Entity extends object, TheKeyFields extends KeyFields<Entity>>
(entity: Entity, keyFields: TheKeyFields): Pick<Entity, TheKeyFields[keyof TheKeyFields]> {
const ret = {} as Pick<Entity, TheKeyFields[keyof TheKeyFields]>;
(Object.keys(keyFields) as Array<keyof TheKeyFields>)
.map((k) => keyFields[k]).forEach(k => ret[k] = entity[k]);
return ret;
}
请指点一下?
如果您只对根据输入类型计算输出类型感兴趣,您将最终使用 Pick
。首先让我们定义输入类型:
const someEntity = {
id: 'someId',
createdAt: 123234223453,
name: 'Dudemanson',
}
type SomeEntityType = typeof someEntity;
const keyMembers = {
hash: 'id',
range: 'createdAt',
} as const; // <-- need that, or values will just be string
type KeyMembersType = typeof keyMembers;
请注意,您需要类似 const
assertion to have the compiler remember that the property values of keyMembers
are of string literal types 的东西,例如 "id"
和 "createdAt"
,而不是无用的 string
。
并且输出类型最终在对象类型 SomeEntityType
上使用 Pick
,键来自 属性 values 24=],像这样:
type ExtractedDbKeyType =
Pick<SomeEntityType, KeyMembersType[keyof KeyMembersType]>;
/* type ExtractedDbKeyType = {
id: string;
createdAt: number;
}*/
如果你想写一个像这样工作的函数签名,你可以这样做:
function extractKeys<T extends object, K extends Record<keyof K, keyof T>>(
entity: T,
keys: K
) {
const ret = {} as Pick<T, K[keyof K]>;
(Object.keys(keys) as Array<keyof K>).
map((k) => keys[k]).forEach(k => ret[k] = entity[k]);
// Without the following assertion you get an output type like
// 'Pick<{...},"..."|"..."">' instead of a more straightforward object type.
return ret as any as (typeof ret extends infer O ? { [P in keyof O]: O[P] } : never);
}
实施只是一个例子;重要的是它采用 T
和 K
类型的值(其中 K
受到限制,因此其值来自 keyof T
)和 return 的值输入 Pick<T, K[keyof K]>
。 return 类型有点问题;如果你只是 return Pick<T, K[keyof K]>
,它往往会显示为 Pick<BlahBlah, "x"|"y"|"z">
(至少在我的 IDE 中是这样)。所以最后我使用映射条件类型将对象类型扩展为更直接的对象类型。像这样:
const extractedDbKey = extractKeys(someEntity, keyMembers);
/* TYPE WITH ASSERTION:
const extractedDbKey: {
id: string;
createdAt: number;
} */
/* TYPE WITHOUT ASSERTION:
const extractedDbKey: Pick<{
id: string;
createdAt: number;
name: string;
}, "id" | "createdAt"> */
可以看出前者更好看。不过这是否重要取决于您。
哦,至少在你的例子中实现是合理的:
console.log(extractedDbKey) // {id: "someId", createdAt: 123234223453}
好的,希望对您有所帮助;祝你好运!
编辑:如果您特别想强制执行 keyMembers
具有属性 hash
和 range
,您可以修改 extractKeys()
:
function extractKeys<T extends object, K extends keyof T>(
entity: T,
keys: { hash: K, range: K }
) {
const ret = {
[keys.hash]: entity[keys.hash],
[keys.range]: entity[keys.range]
} as Pick<T, K>;
return ret as any as (typeof ret extends infer O ? { [P in keyof O]: O[P] } : never);
}
干杯!