Skip to content

Remove key from object after delete object.key #53697

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
5 tasks done
ScrapsPrograms opened this issue Apr 7, 2023 · 5 comments
Open
5 tasks done

Remove key from object after delete object.key #53697

ScrapsPrograms opened this issue Apr 7, 2023 · 5 comments
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript

Comments

@ScrapsPrograms
Copy link

ScrapsPrograms commented Apr 7, 2023

Suggestion

πŸ” Search Terms

delete
delete key
narrowing
narrowing delete

βœ… Viability Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

In TypeScript, it's already possible to have objects add keys to their types using if ("key" in object) // object: { key: unknown }. It is not currently possible to do the exact opposite: deleting a key off of an object.

This could be achieved in two ways:

  • Guaranteed: type { key: string }; delete object.key; type { }.
  • Optional: type { key?: string | undefined }; if (object.key === undefined) delete object.key; type { key?: string }.

The guaranteed way is pretty straight forward: it removes the key entirely.
The optional way is slightly more tricky: it should only remove the type information checked in the if statement. Remaining type options should still exist after the if statement. If the last remaining type option is removed, then it should function the same as the guaranteed way.

Naturally, if the key didn't exist on the object in the first place, the type information should remain the exact same as before.

πŸ“ƒ Motivating Example

I'm using the zod library, with which I set a value on object user as { givenName: string; infix?: string | undefined; surname: string; }.
I wrote a factory function toUser({ givenName, infix, surname }: { givenName: string; infix?: string; surname: string; }): User { ... }.
The type infix?: string | undefined is not compatible with type infix?: string.
image

--- EDIT: Extra example ---
After executing delete object.key, I'm expecting the type information to no longer contain this key:

  const myObject: { key1?: string | undefined, key2?: string | undefined } = { ... };
  delete myObject.key1; // Type should now become: { key2?: string | undefined}
  myObject.key1 // Should error, key was deleted

Now, if I want to remove a key under certain conditions:

  const myObject: { key1?: string | number | undefined } = { ... };
  if (typeof myObject.key1 === "number") delete myObject.key1; // Removes "number" from types. This already works.
  if (typeof myObject.key1 === "undefined") delete myObject.key1; // Removes "undefined" from type definition. This does not work yet.
  if (typeof myObject.key1 === "string") delete myObject.key1; // Removes the last type; "key1" will no longer exist on object.

In the above example, the native types "number" and "string" get stripped off as expected. The type "undefined" however, does not. This breaks the tsconfig option exactOptionalPropertyTypes as the key should now no longer be able to exist. It cannot exist with literal value undefined.

πŸ’» Use Cases

This is purely type information, but it seems essential if the typing system is to remain correct after the delete keyword.
For the time being, I'll have to set the type to infix?: string | undefined, knowing it's not entirely accurate (key "infix" should not be permitted with value "undefined").

@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature labels Apr 7, 2023
@RyanCavanaugh
Copy link
Member

A more clearly-stated example would be nice

@ScrapsPrograms
Copy link
Author

Sorry. I've added a new example with some comments after it; hope that clarifies things a bit more.

@fatcerberus
Copy link

Optional: type { key?: string | undefined }; if (object.key === undefined) delete object.key; type { key?: string }.

I don't think this actually matters, regardless of exactOptionalPropertyTypes. From the point of view of someone reading the value of obj.key, there's no meaningful difference between { key?: string } vs. { key?: string | undefined } - it only matters when you're writing to that property, in which case the declared type is all that matters and whatever narrowing has happened since then is irrelevant.

@ScrapsPrograms
Copy link
Author

Perhaps, but what I mostly would like to see is a type update where the key is omitted after the delete statement. It's not so much about the value being string | undefined, that was mostly just an example.

It does seem to me it will need to include undefined if it is to figure the proper type. There's a very small difference between "key" in myObject and myObject.key === undefined. It's possible to have a situation where both are true, or "key" in myObject is false.

Essentially, there are two situations to consider.

  • Narrowing typing information (mostly working already, i.e. with "string", "number", etc.)
  • Deleting the key entirely

Narrowing
When narrowing, it's important to consider that it's possible for a key to exist with specific value undefined. This is displayed as myObject.key = undefined. The key exists, but has no value. The type of myObject is { key: undefined }. Note that it does not need to be optional to have value undefined.

In case of a type union, such as string | undefined, a conditional delete should turn the key optional. After all, it cannot be part of the object based on the condition. Here's an example:

  type MyObjectType = { key: string | undefined };
  const myObject: MyObjectType = { key: undefined };

  if (typeof myObject.key === "undefined") delete myObject.key; // After this, the type should be { key?: string }
  const value: string = myObject.key // Should error as "key" could be missing from the type.

If the situation is such, where "key" is optional, TypeScript will need to consider the possibility of the key being absent at the start.

  type MyObjectType = { key?: string | undefined };
  const myObject: MyObjectType = { key: undefined };

  if (typeof myObject.key === "undefined") delete myObject.key; // Type should become { key?: string }
  const value: string | undefined = myObject.key // Should be okay
  const value2: string = myObject.key // Should error; "key" may be undefined

  if ("key" in myObject) delete myObject.key; // Key is removed and type should become { }
  const value: string | undefined = myObject.key; // Should error; "key" cannot be string
  const value2: undefined = myObject.key; // Should be okay, "key" does not exist. typeof "key" is undefined.

Deleting the key entirely
Deleting entirely is simply a matter of omitting the key from the original type entirely.

  type MyObjectType = { key: string | undefined };
  const myObject: MyObjectType = { key: "Hello World!" };

  delete myObject.key // After this, the type should be { }
  const value: string = myObject.key // Should error as "key" does not exist on type of "myObject".

@Vivekyadav-4321
Copy link

nice

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

4 participants