部分重叠的打字稿键
Typescript keyof overlap of Partial
我想输入一个允许默认值的数据库插入函数:
(我正在使用 type-fest 的 SetOptional)
function defaultInsert<T, D = Partial<T>>(data: SetOptional<T, keyof D>, defaults: D) {
我想用 SetOptional 说的是,数据对象应该包含 T 中的任何内容,默认值 (D) 中未指定,但允许覆盖默认值中的任何内容。
遗憾的是,这会导致以下错误
Type 'keyof D' does not satisfy the constraint 'keyof T'.
Type 'string | number | symbol' is not assignable to type 'keyof T'.
Type 'string' is not assignable to type 'keyof T'.(2344)
Typescript playground 更详细的示例。
有什么方法可以过滤掉部分以仅包含 T 的键,因为我假设这是由于 D 可能包含不在 T 中的多余属性而导致的问题?
非常感谢
编辑:根据@Alex Chashin 的建议,另一个TS Playground 使用键作为定义默认值的方式。
EDIT2:使用 UPD2 @Alex Chashin
中提供的解决方案更新了 playground
EDIT3:更新了前面的示例,以便也可以省略默认值:playground
UPD2:
所以我唯一能想出的,在这个世界上并不太复杂的是这个(ts playground):
function defaultInsert<T, D extends keyof T>(
data: Omit<T, D> & Partial<Pick<T, D>>,
defaults: Pick<T, D>
): void {
// Hover over this variable to see its type
const doc = { ...data, ...defaults } as T
insert<T>(doc)
}
这通过了您提供的测试,但不幸的是 {...data, ...default}
未被推断为 T
,因此您必须对其进行转换。不过我认为这没什么大不了的,因为这种类型转换只需要 4 个符号。唯一的问题是你必须每次都明确地为 D
提供密钥,因为你需要在示例中编写 keyof typeof personDefaults
更新:
Link 使用一种可能的解决方案到 TS 游乐场:playgroung
原回答:
所以Partial<T>
已经使T
的所有键成为可选的,你不需要在Partial
之后使用SetOptional
。如果您希望 all 数据键是可选的,只需使用:
function defaultInsert<T>(data: Partial<T>, defaults: T) {
// ...
}
请注意 defaults
的类型为 T
,而不是 Partial<T>
,因为如果它是 Partial<T>
,您可以提供不完整的文档 data
和不完整的文档 defaults
,因此合并它们不会得到完整的文档,这对您来说可能是个问题。如果不是,请将T
替换为Partial<T>
,很难判断,因为您没有提供所有代码。
如果您只希望某些键是可选的,请执行以下操作:
function defaultInsert<T, Keys extends string>(
data: SetOptional<T, Keys>,
defaults: { [key in Keys]: T[key] }
) {
// ...
}
像这样,您只需要为可选键提供默认值,而不是为所有键提供默认值
您还可以将 defaults
设为
Omit<Partial<T>, Keys> & { [key in Keys]: T[key] }
// or if you use your `SetOptional`
SetOptional<T, Exclude<keyof T, Keys>>
这样打字稿就不会抱怨,当你为非可选键提供默认值时
然后使用 like
defaultInsert<{ foo: number, bar: string }, 'foo'>(
{ bar: 'abc' },
{ foo: 12345 }
)
我想输入一个允许默认值的数据库插入函数: (我正在使用 type-fest 的 SetOptional)
function defaultInsert<T, D = Partial<T>>(data: SetOptional<T, keyof D>, defaults: D) {
我想用 SetOptional 说的是,数据对象应该包含 T 中的任何内容,默认值 (D) 中未指定,但允许覆盖默认值中的任何内容。
遗憾的是,这会导致以下错误
Type 'keyof D' does not satisfy the constraint 'keyof T'.
Type 'string | number | symbol' is not assignable to type 'keyof T'.
Type 'string' is not assignable to type 'keyof T'.(2344)
Typescript playground 更详细的示例。
有什么方法可以过滤掉部分以仅包含 T 的键,因为我假设这是由于 D 可能包含不在 T 中的多余属性而导致的问题?
非常感谢
编辑:根据@Alex Chashin 的建议,另一个TS Playground 使用键作为定义默认值的方式。
EDIT2:使用 UPD2 @Alex Chashin
中提供的解决方案更新了 playgroundEDIT3:更新了前面的示例,以便也可以省略默认值:playground
UPD2: 所以我唯一能想出的,在这个世界上并不太复杂的是这个(ts playground):
function defaultInsert<T, D extends keyof T>(
data: Omit<T, D> & Partial<Pick<T, D>>,
defaults: Pick<T, D>
): void {
// Hover over this variable to see its type
const doc = { ...data, ...defaults } as T
insert<T>(doc)
}
这通过了您提供的测试,但不幸的是 {...data, ...default}
未被推断为 T
,因此您必须对其进行转换。不过我认为这没什么大不了的,因为这种类型转换只需要 4 个符号。唯一的问题是你必须每次都明确地为 D
提供密钥,因为你需要在示例中编写 keyof typeof personDefaults
更新:
Link 使用一种可能的解决方案到 TS 游乐场:playgroung
原回答:
所以Partial<T>
已经使T
的所有键成为可选的,你不需要在Partial
之后使用SetOptional
。如果您希望 all 数据键是可选的,只需使用:
function defaultInsert<T>(data: Partial<T>, defaults: T) {
// ...
}
请注意 defaults
的类型为 T
,而不是 Partial<T>
,因为如果它是 Partial<T>
,您可以提供不完整的文档 data
和不完整的文档 defaults
,因此合并它们不会得到完整的文档,这对您来说可能是个问题。如果不是,请将T
替换为Partial<T>
,很难判断,因为您没有提供所有代码。
如果您只希望某些键是可选的,请执行以下操作:
function defaultInsert<T, Keys extends string>(
data: SetOptional<T, Keys>,
defaults: { [key in Keys]: T[key] }
) {
// ...
}
像这样,您只需要为可选键提供默认值,而不是为所有键提供默认值
您还可以将 defaults
设为
Omit<Partial<T>, Keys> & { [key in Keys]: T[key] }
// or if you use your `SetOptional`
SetOptional<T, Exclude<keyof T, Keys>>
这样打字稿就不会抱怨,当你为非可选键提供默认值时
然后使用 like
defaultInsert<{ foo: number, bar: string }, 'foo'>(
{ bar: 'abc' },
{ foo: 12345 }
)