首页 > 解决方案 > 如何定义不包含特定成员的对象类型

问题描述

我正在处理我放入和取出数据库/操作的对象,有时希望添加和删除特定于数据库/操作的字段。

例如,我有:

type EntryEvent<EntryLine> = { type: 'entry' } & EntryLine;

在其他地方,我希望删除该type成员以便将一个EntryEvent<EntryLine>back 变成一个EntryLine. 为此,我使用如下实用程序函数:

export function stripFields<T extends object, K extends keyof T>(
  o: T,
  fields: K[]
): Omit<T, K> {
  const result: Partial<T> = { ...(<object>o) };
  for (const field of fields) {
    delete result[field];
  }
  return <Omit<T, K>>result;
}

问题是,生成的对象不会被识别为EntryLine.

如果我尝试将以下内容分配给EntryLine类型:

stripFields(event, ['type'])

我会得到:

Argument of type 'Pick<EntryEvent<EntryLine>, Exclude<keyof EntryLine, "type">>' is not assignable to parameter of type 'EntryLine'.
  'Pick<EntryEvent<EntryLine>, Exclude<keyof EntryLine, "type">>' is assignable to the constraint of type 'EntryLine', but 'EntryLine' could be instantiated with a different subtype of constraint 'Pick<object, never>' ts(2345)

据我所知,这是有道理的,因为如果泛型类型EntryLine包含一个type成员,结果stripFields将不再是一个EntryLine(因为它会丢失它的type成员)。

有什么方法可以定义约束泛型类型EntryLine,使其不允许具有type字段的对象?还是有其他解决方法?

更新:

以下代码为我重现了该问题:

type EntryEvent<EntryLine> = { type: "entry" } & EntryLine;

type MyEntryLine = {
  a: Date;
};

export function stripFields<T extends object, K extends keyof T>(
  o: T,
  fields: K[]
): Omit<T, K> {
  const result: Partial<T> = { ...(<object>o) };
  for (const field of fields) {
    delete result[field];
  }
  return <Omit<T, K>>result;
}

type Callback<EntryLine> = (a: EntryLine) => void;

function thingThatTakesAnEvent<EntryLine>(a: EntryEvent<EntryLine>, callback: Callback<EntryLine>) {
  callback(stripFields(a, ["type"]));
}

function thingThatTakesAMyEntryLine(a: MyEntryLine) {
  console.log(a.a);
}

declare let event: EntryEvent<MyEntryLine>;

thingThatTakesAnEvent(event, thingThatTakesAMyEntryLine);

这里的游乐场

标签: typescript

解决方案


'Pick< EntryEvent< EntryLine>, Exclude< keyof EntryLine, "type">>' 可分配给类型为“EntryLine”的约束,但可以使用约束“{}”的不同子类型来实例化“EntryLine”。

这种形式的错误消息基本上意味着您试图从传入的泛型类型参数中推断出太多,而只有调用者/客户端才能知道其确切类型。Typescript 不再认为您对其类型的猜测是安全的。

关于你的代码,它可以被翻译成这样:

function thingThatTakesAnEvent<EntryLine>(
  a: EntryEvent<EntryLine>,
  callback: Callback<EntryLine>
) {
  callback(stripFields(a, ["type"]));
           //------ error ------//
}

==> 的返回类型stripFields,即Pick< EntryEvent< EntryLine>, Exclude< keyof EntryLine, "type">>,可分配给约束EntryLine(此处默认为{}),但是如果调用者像这样调用函数怎么办:thingThatTakesAnEvent<{foo: "bar"}>?在函数内部,我们不知道它是{foo: "bar"},我们应该调用callbackwith EntryLine,但是我们使用了一个特定的子类型,这可能不是来自调用者的正确子类型。

根据目的,您可以执行以下操作:

  1. 更改您的回调类型以匹配stripFields Playground的类型:
type Callback<EntryLine> = (a: Omit<EntryEvent<EntryLine>, "type">) => void;
  1. thingThatTakesAndEvent Playground中进行演员表:
function thingThatTakesAnEvent<EntryLine>(
  a: EntryEvent<EntryLine>,
  callback: Callback<EntryLine>
) {
  callback((stripFields(a, ["type"]) as unknown) as EntryLine);
}

推荐阅读