简体   繁体   中英

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

If I try to assign the following to an EntryLine type:

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

Is there some way I can define constrain the generic type EntryLine so that it does not allow objects with a type field? 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 '{}'.

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.

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"}> ? 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.

Based on the purpose, you could do the following:

  1. Change your Callback type to match the type of stripFields Playground :
type Callback<EntryLine> = (a: Omit<EntryEvent<EntryLine>, "type">) => void;
  1. Do a cast in thingThatTakesAndEvent Playground :
function thingThatTakesAnEvent<EntryLine>(
  a: EntryEvent<EntryLine>,
  callback: Callback<EntryLine>
) {
  callback((stripFields(a, ["type"]) as unknown) as EntryLine);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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