部分重叠的打字稿键

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 }
)