部分重叠的打字稿键

时间:2020-09-26 13:12:59

标签: typescript keyof

我想输入一个允许默认值的数据库插入函数: (我正在使用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以获得更详细的示例。

我是否有任何办法可以将Partial过滤为仅包含T的键,因为我假设这是问题所在,原因是D可能包含不在T中的多余属性?

非常感谢

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

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

EDIT3:更新了前面的示例,因此默认值也可以省略:playground

1 个答案:

答案 0 :(得分: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时,都必须根据需要为keyof typeof personDefaults显式提供密钥。 UPD: 使用一种可能的解决方案链接到TS游乐场:playgroung

原始答案:

因此,Partial<T>已经使T的所有键为可选,因此在执行SetOptional之后,您无需使用Partial。如果您希望所有数据键是可选的,请使用以下命令:

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

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

然后使用

defaultInsert<{ foo: number, bar: string }, 'foo'>(
  { bar: 'abc' },
  { foo: 12345 }
)