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