0

I'm trying to delete the password key in a user object in a typesafe way but typescript keeps complaining.

const user = db.findUnique({where: email:input.email});

// returning user without password
// OPTION 1 - eslint disable doesn't remove the yellow squiggly on the password variable
// @eslint-disable-next-line @typescript-eslint/no-unused-vars
const { password, ...userWithoutPassword } = user;
return userWithoutPassword;

// OPTION 2 - Unsafe return of an `any` typed value.eslint@typescript-eslint/no-unsafe-assignment
return omit(user, "password"); // using lodash
// this also leads to the same error
const userWithoutPassword: Omit<User, "password"> = omit(user, "password");
return userWithoutPassword;

// OPTION 3 - The operand of a 'delete' operator must be optional.
// I don't want to change the type definition of User to make password optional. It should be a new type
delete user.password;
return user;

What's the appropriate way to do this?

wongz
  • 3,255
  • 2
  • 28
  • 55
  • `return {...user, password: undefined};` works but doesn't really satisfy the criteria of the question which is to delete the key and return a type without the password key – wongz Jan 19 '23 at 23:47
  • You should probably stick to the first option. Check out the `ignoreRestSiblings` option: https://stackoverflow.com/questions/56151661/omit-property-variable-when-using-object-destructuring – Tobias S. Jan 19 '23 at 23:50
  • @TobiasS. Thanks. That works. The correct line to disable it I realized is `// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars` – wongz Jan 20 '23 at 00:13
  • Does something like [this](https://tsplay.dev/WvaRRm) answer your question? If so, I can write it up as an answer. If not, what am I missing? – jsejcksn Jan 20 '23 at 08:44
  • @jsejcksn Oh nice. Yeah that works. I changed it to delete password [here](https://tsplay.dev/wE4JkW). Can you clarify this line in the explanation `user as Partial>`. `user` still stays as type `User` when hovering but when I read the code it reads like `user` should be `{password?: string}` and that's it, since it's saying only the password field should be partial... so are the other keys still included, or maybe I'm misunderstanding the "as" functionality – wongz Jan 22 '23 at 01:12

1 Answers1

0

The return type for lodash's omit (source) should produce a type that is structurally compatible with the desired type Omit<User, "password">. It sounds like there might be a configuration issue in your project if you are seeing any as the return type of the omit function. (Have you installed @types/lodash in your project?) See example in this playground.

If, for some reason, you want to mutate the existing user object by deleting the password property (instead of creating a new object), you will have to use some type assertions in order to satisfy the compiler because it does not allow for modifying types in-place — see functional predicates (a.k.a. type guards) for more.

Here's an example:

TS Playground

type User = {
  name: string;
  id: string;
  email: string;
  password: string;
  // etc.
};

function getUserWithoutPassword (): Omit<User, "password"> {
  // const user = db.findUnique({where: email:input.email});
  const user: User = {
    name: "Foo",
    id: "abc123",
    email: "foo@bar.baz",
    password: "1_bad_password",
  };

  delete (user as Partial<Pick<User, "password">>).password;
  return user as Omit<User, "password">;
}


delete (user as Partial<Pick<User, "password">>).password;
//     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

In the statement above, user is being asserted as the type { password?: string }, so the compiler will allow deletion of the optional value.

return user as Omit<User, "password">;
//     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

And in the return statement, user is being asserted as a type that represents the result of the previous deletion operation.

jsejcksn
  • 27,667
  • 4
  • 38
  • 62