简体   繁体   English

如何定义不包含特定成员的对象类型

[英]How to define an object type that does _not_ include a specific member

I am working with objects I put in and out of databases/actions and sometimes wish to add and remove database/action-specific fields. 我正在处理我放入和退出数据库/动作的对象,有时希望添加和删除特定于数据库/动作的字段。

For example, I have: 例如,我有:

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

Elsewhere I wish to drop the type member in order to turn an EntryEvent<EntryLine> back into an EntryLine . 在其他地方,我希望删除type成员,以便将EntryEvent<EntryLine>EntryLine To do that I use a utility function as follows: 为此,我使用以下实用程序函数:

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

The trouble is, the resulting object won't be recognized as an EntryLine . 麻烦的是,生成的对象将不会被识别为EntryLine

If I try to assign the following to an EntryLine type: 如果我尝试将以下内容分配给EntryLine类型:

stripFields(event, ['type'])

I will get: 我会得到:

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)

As far as I can tell that makes sense, because if the generic type EntryLine included a type member, the result of stripFields would no longer be an EntryLine (since it would be missing its type member). 据我所知,这是有道理的,因为如果通用类型EntryLine包含type成员,则stripFields的结果将不再是EntryLine (因为它将丢失其type成员)。

Is there some way I can define constrain the generic type EntryLine so that it does not allow objects with a type field? 有什么方法可以定义约束通用类型EntryLine以便它不允许带有type字段的对象? Or is there some other means of working around this? 还是有其他解决方法?

Update: 更新:

The following code reproduces the problem for me: 以下代码为我重现了该问题:

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

Playground here 这里的游乐场

'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 <EntryEvent <EntryLine>,Exclude <EntryLine的键,“类型” >>”可分配给类型“ EntryLine”的约束,但是可以使用约束“ {}”的另一个子类型实例化“ EntryLine”。

Error messages in this form basically mean, that you tried to infer too much from the passed in generic type parameter, whose exact type only the caller/client can know. 这种形式的错误消息基本上意味着,您试图从传入的泛型类型参数中推断出太多信息,而泛型类型参数只有调用者/客户端才能知道。 Typescript does not consider your speculations about its type to be safe anymore. Typescript不再认为您对它的类型的猜测是安全的。

Concerning your code, it could be translated to this: 关于您的代码,可以将其翻译为:

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

==> the return type of stripFields , which is Pick< EntryEvent< EntryLine>, Exclude< keyof EntryLine, "type">> , is assignable to the constraint EntryLine (which defaults here to {} ), but what if the caller invoked the function like this: thingThatTakesAnEvent<{foo: "bar"}> ? ==> stripFields的返回类型为Pick< EntryEvent< EntryLine>, Exclude< keyof EntryLine, "type">> ,可以分配给约束EntryLine (默认为{} ),但是如果调用者调用了该方法像这样的函数: thingThatTakesAnEvent<{foo: "bar"}> Inside the function we cannot know that it's {foo: "bar"} , and we should invoke callback with EntryLine , but we did with a specific subtype, which may be not the right one from the caller. 在函数内部,我们不知道它是{foo: "bar"} ,我们应该使用EntryLine调用callback ,但是我们使用了特定的子类型,这可能与调用者不EntryLine

Based on the purpose, you could do the following: 根据目的,您可以执行以下操作:

  1. Change your Callback type to match the type of stripFields Playground : 更改您的回调类型以匹配stripFields Playground的类型:
type Callback<EntryLine> = (a: Omit<EntryEvent<EntryLine>, "type">) => void;
  1. Do a cast in thingThatTakesAndEvent Playground : thingThatTakesAndEvent Playground中进行转换
function thingThatTakesAnEvent<EntryLine>(
  a: EntryEvent<EntryLine>,
  callback: Callback<EntryLine>
) {
  callback((stripFields(a, ["type"]) as unknown) as EntryLine);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM