0

我想输入一个允许默认值的数据库插入函数:(我正在使用 setOptional 的type-fest

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)

打字稿游乐场以获得更详细的示例。

有什么方法可以过滤 Partial 以仅包含 T 的键,因为我假设这是由于 D 可能包含不在 T 中的多余属性的问题?

非常感谢

编辑:作为@Alex Chashin 的建议,另一个TS Playground使用键作为定义默认值的方式。

EDIT2:使用 UPD2 @Alex Chashin 中提供的解决方案更新了游乐场

EDIT3:更新了前面的示例,以便也可以省略默认值:操场

4

1 回答 1

0

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唯一的问题是,在编写 UPD 的示例中,您必须每次都明确提供密钥keyof typeof personDefaults :使用一种可能的解决方案链接到 TS 操场:playgroung

原答案:

所以Partial<T>已经让所有的键都是可选的,你做完之后T就不需要使用了。如果您希望所有数据键都是可选的,请使用:SetOptionalPartial

function defaultInsert<T>(data: Partial<T>, defaults: T) {
  // ...
}

请注意,defaults有 type T, not Partial<T>,因为如果是,Partial<T>您可以提供不完整的data文档 as 和不完整的文档 as defaults,因此合并它们不会给出完整的文档,这对您来说可能是个问题。如果不是,请替换TPartial<T>,很难判断,因为您没有提供所有代码。

如果您只希望某些键是可选的,请执行以下操作:

function defaultInsert<T, Keys extends string>(
  data: SetOptional<T, Keys>, 
  defaults: { [key in Keys]: T[key] }
) {
  // ...
}

像这样,您只需要为可选键提供默认值,而不是为所有键提供默认值您也可以defaults使用 type

Omit<Partial<T>, Keys> & { [key in Keys]: T[key] }
// or if you use your `SetOptional`
SetOptional<T, Exclude<keyof T, Keys>>

因此,当您为非可选键提供默认值时,打字稿不会抱怨

然后像

defaultInsert<{ foo: number, bar: string }, 'foo'>(
  { bar: 'abc' },
  { foo: 12345 }
)
于 2020-09-26T13:31:15.780 回答