-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Destructuring issue after removing a keyed property #57614
Comments
I'm trying to reduce the cognitive load here by replacing indirection with direct versions. I see function removeRecordWithKey<
T extends object,
K extends string | number
>(stateModels: Record<K, T>, model: T): Record<K, T> {
const id = '' as K
const { [id]: deletedEntity, ...models } = stateModels;
return models;
}; and I wonder... why would you expcect |
@jcalz Based on the βworkingβ example in the OP (which involves a |
@jcalz @fatcerberus Thanks for your feedback. I think have created an isolated version of the issue using the input from both.
I'm updating the example with
I've realice that, and I considered it a feature for my use case, asserting that the id has the required type. But I'm aware of the complexity this could introduce now. I updated the Issue Description with the updated version of the example, but I'll leave here in case you are checking this from somewhere else: declare function getId<Model, Id>(model: Model): Id;
function removeRecord<
Model extends object,
Id extends 'foo' | 'bar'
>(stateModels: Record<Id, Model>, model: Model): Record<Id, Model> {
const id = getId<Model, Id>(model);
const { [id]: deletedEntity, ...models } = stateModels;
return models;
}; |
If you call |
Yes, that's true. But I think that with this information the resulting type of Maybe I am mistaken, but, mathematically speaking, if I have a set of
|
That's not how generics work. When you write e.g. function foo<T>(x: T): T {
/* ... */
} This can only legally be an identity function, because there's an implicit universal quantifier on |
In any case this isnβt a bug in TypeScript, so you might want to close the issue to save the TS team the effort of triaging it. After that if you still want to discuss it here, thatβs fine, although questions are better asked elsewhere (like Stack Overflow or Discord) |
I still consider this a bug, because the resulting type is an empty object, and as I mentioned, if you remove just one value, you won't be removing the whole set. I updated the description to consider that behavior is also a bug. Thanks. |
? I understand the supposed bug less now than I did before. Itβs always valid to assign any non-nullish value to the empty object type. Iβd explain why but I think Iβd better leave this issue to the TS team to handle. |
In other words you would expect either Not that I don't understand that |
You seem to believe that I don't see any bug demonstrated here. |
Yes, I think using an empty interface doesn't provide information about what I would expect, its not about In my original example, I created an alternative version that worked as I would expect, something like: declare function getId<Model, Id>(model: Model): Id;
type ModelWithStringKey<Model> = Record<string, Model>;
function removeRecordString<
Model extends object,
>(stateModels: ModelWithStringKey<Model>, model: Model): ModelWithStringKey<Model> {
const id = getId<Model, string>(model);
const { [id]: deletedEntity, ...models } = stateModels;
return models;
}; In this example, the const models: {
[x: string]: Model;
} But in the other example: declare function getId<Model, Id>(model: Model): Id;
function removeRecord<
Model extends object,
Id extends string
>(stateModels: Record<Id, Model>, model: Model): Record<Id, Model> {
const id = getId<Model, Id>(model);
const { [id]: deletedEntity, ...models } = stateModels;
return models;
}; The Omit<Record<Id, Model>, Id> So, this is now an empty object Is this expected? I have this issue with a project I'm creating, it's a minor issue, but I thought it was a problem with TS. I don't think the behavior should change like that only because I'm using a generic param for the key instead of a literal. Maybe the issue is related to not being able to restrict the generic between literal type, or maybe it's not an issue. I still think it should be an issue. |
Let's say that this function const obj = { foo: {}, bar: { }};
const p = removeRecord(obj, {});
p.bar; // ok
p.foo; // ok It's hard to reason about this because |
First, thank you all. Know that I value your time, and I appreciate your input. Maybe I tried to simplify it too much. Here is a playground of the issue that I'm having: I tried to extract the important parts and reduce the issue to the minimal possible scenario. I don't know if it has noise around that can be removed, but I'll let everything around to see if this can be considered a bug as it is. This is the error that I'm having:
And I consider that an issue for the reasons above. Suppose I have a partial object with an indeterminate number of properties, and I take from it one property away from a set of indeterminate possibilities. In that case, the resulting object should be assigned to the same partial object. Maybe this scenario explains better what I'm trying to do. If this is still insufficient to be considered a bug, I understand and close this, even when I still consider this an unexpected behavior π . But this is the specific scenario I'm having the issue with, and for me, the workaround is just casting to the type I would expect. |
Now this feels like #54508 and the issues linked within. |
Ah, I see now, the
|
This issue has been marked as "Working as Intended" and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
π Search Terms
destructure omit
π Version & Regression Information
β― Playground Link
Updated link. A simplified version, with the resulting type as an example.
https://www.typescriptlang.org/play?#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXwHMQMBJYAHgFkdQIAaeMgPgAoBbGkCALnmtoCUvMgG4AUGJTpseeHA4A3EACVwOGBTHw+nCPBAAPDCFTAAzvBwAjAFbgMdLY2D6jJ8-ADkiHDk-wAHy8rWE8xVjMMKGN+LjNeVTB1CjIGWIgmBg5aXnSheETk8lSdWiZ4AG8nJNRI+CwXAF5CYjIqXQZmdl0BcWq8Oor4AG0GgF1eWmIQYABRVGwMAE8GADp17Lj4AF94ZsjokHSzPu04DGQYfE2IE7FtvqwFkBhEKDAEWbYAB2XCjUq2wkUkwuHw8hwSn+wAA6uoANZPAjkJzpVzGUwWax2TCObRkdHuCzeXz+IKeEIwMIRKIxXTxApqDTFYBpXSZeA3XI9XhfX5LaGVfq1DD1JotUgUdKdYCsG69CTaGqDEbjSZcaZzBZYZZrDb0nZ7eAHOm0O5Oc6Xa708QPIA
Updated link (simplified version)
Original link
π» Code
Updated example:
Original Example:
π Actual behavior
And error is thrown in the return statement:
The result type is an empty object
π Expected behavior
No error should be thrown, because an object with a removed keyed property, should be equal to the same object.
Additional information about the issue
When you don't use a keyed property, the code is valid:
The Playground link for "Original example" has different examples for more complex and simpler scenarios.
The text was updated successfully, but these errors were encountered: