-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Using rest destructing on class objects seems to include getters in the resulting type #46967
Comments
Workaround: Use the specified class type instead of the polymorphic toClient() {
const { password, ...client } = this as User
return {
score: this.score, // OK
...client
}
} |
@whzx5byb Can you explain that a bit, please? Why is |
|
Thanks for giving a version range where this stopped working; I was able to bisect this to #37192. |
Hm, that PR is actually just what expanded out the error message; the core issue that rest destructuring Here's a modified example that shows this behavior: class User {
constructor(
public uid: number,
public password: string // sensitive info
) { }
get score(): number {
return 10; // example code - imagine having to fetch something a little more complex
}
toClient(): { uid: number; score?: never } {
const { password: _, ...client } = this;
return client; // Type 'this["score"]' is not assignable to type 'undefined'.
}
toClient2(): { uid: number; score?: never } {
const { password: _, ...client } = this as User;
return client; // OK
}
} With the desired behavior being that both |
I was able to "fix" the original case by not using class A {
constructor(public foo: string) {}
get bar(): number {
return 1;
}
func() {
const { ...rest1 } = this;
const { ...rest2 } = this as A;
const { foo: _f1, ...rest3 } = this;
const { foo: _f2, ...rest4 } = this as A;
// Rest destructuring drops properties provided by getters.
// "bar" should not be present in any of these.
rest1.bar;
rest2.bar;
rest3.bar;
rest4.bar;
}
}
function destructure<T extends A>(x: T) {
const { ...rest1 } = x;
const { ...rest2 } = x as A;
const { foo: _f1, ...rest3 } = x;
const { foo: _f2, ...rest4 } = x as A;
// Rest destructuring drops properties provided by getters.
// "bar" should not be present in any of these.
rest1.bar;
rest2.bar;
rest3.bar;
rest4.bar;
} Where all 8 accesses to |
Hi @jakebailey , Thanks for looking at this, and getting a fix so quickly. To be clear, in your last Playground link, it'll error on all 8 access to |
That's what my PR does, yes. You can try out my PR here: Playground Link There's unfortunately a little more nuance that my PR needs to handle better, namely, it should probably be legal to destructure non-public properties when it's I'll make sure we discuss it before merging. |
Per #47078 (comment), dropping non-public seems correct, so the behavior I'm showing is the final one. |
Thanks again, @jakebailey and @RyanCavanaugh for the quick turnaround! 👍 |
This seems like an unsafe assumption. Whether a property is copied into a rest pattern depends on whether it is an own property, not on whether a property is a getter or a method. As far as I understand, TypeScript doesn't distinguish own from inherited properties? |
Bug Report
If you have a class along the lines of:
If we then want to destructure a
User
object (e.g. returning a client-friendly basic object from a server), we first declare a type:We pass
score
to theOmit
here because, as we're using destructing, only basic properties will be returned, not any getters. We can then create our method:However,
score
is something that we want to return, so we change ourUserClient
type to:and our
toClient()
method now becomes:This, however, gives the error
'score' is specified more than once, so this usage will be overwritten
, even though when we destructure an object, it doesn't give us the getters - it seems like the inferred type from this line:is wrong (specifically, it's
Omit<this, 'password'>
forclient
). In order to fix it, I need to change that line toThis doesn't really have any effect on the underlying JavaScript code, so it's a minor bug (I think)
🔎 Search Terms
rest
destructuring
🕗 Version & Regression Information
I'm currently on
4.4.3
though running in TypeScript Playground,3.9.7
is when it first seems to show up.In that version, the inferred type is
Pick<this, Exclude<keyof this, "password">>
In 3.8.3 the inferred type is the same, though the error isn't present, so there must have been a change to the underlying
Pick
code (perhaps to include getters?)⏯ Playground Link
https://www.typescriptlang.org/play?ts=4.4.4#code/MYGwhgzhAECqEFMBO0DeAoAkMA9gOwgBckBXYQnJACi0wAcSAjEAS2GhJYBMAuaPEgFtGyADS0GzNtDqQIAd0q9oRJCzwBzaAHptKhARaEWANwTR1AMxxYAlGmgBfLFg0JCK3EgRVbfAcLIaLTehCRIeNAAjAAMANw6eggAHmCCdCDmuFzmALQWgmAa6uYAFmAm6loU0JbuwKUqOILupVXQYNCshISZ0IKUWc0ZKVjO6NCT0BQAwqwGhL588MhzLAvBU1vQuAQeqDJyikhcotAAdJeg63gejtAAvNNtEHET25Oh4ZEYHx8QXgQfEIL3OAMG4j+20u52uC3eH2cW2czkIAE86OYVkg1hsngB5QRGAA82LOAHJZFBjlxydAAD7Qcng7zkgB8DIcLKB-CEIhQjjiQA
💻 Code
🙁 Actual behavior
The inferred type on
client
seems to include getters🙂 Expected behavior
The inferred type on
client
to only include basic properties as it's created through destructuringThe text was updated successfully, but these errors were encountered: