Skip to content
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

Protected instance member inaccessible to same-class constructor via object destructuring assignment #58529

Open
jimmy-zhening-luo opened this issue May 14, 2024 · 2 comments

Comments

@jimmy-zhening-luo
Copy link

🔎 Search Terms

class protected member constructor another instance access destructuring assignment same type typescript

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about each common issue, pre-declined request, behavior that looks wrong but is actually right, etc.

⏯ Playground Link

https://www.typescriptlang.org/play/?#code/CYUwxgNghgTiAEYD2A7AzgF3gNShAlsACpQDmAXPAK4r4COVCaAngLYBGSEA3ALABQoSLAQZmABwQlS8ALzxMMfChkAyeAG94AbVwFiZALqUMMRvAC+ffgOFo08AKpoQMTQPjxxVdgTDw4KGBUCGZ4AAsoFGAIEEpFZVJrT3EYJAxwDOB4AH0MMjRKaW1Daw9EVEUqMAwkNwAKcs8qFxhKZ1cAGib4FBAAd2k0AH5400SS7v5PAEp3ac94RoXF+dXVyOjYk3D8NAA6TZiQKfXFvIKdvf2L0jRO+AB6R-gAUQAld4B5d57PCzk8DEkiQADNegMhnJZPIAEQ0UCg5QgYCwv6LYbUVrozyUDQ49ZHbZY1yHKLHU5nda3QpOVr7MDhcAAayG9X2HL6gwKM0pVMsPRmAh6GF2ByJCHkLVJEuSi1F1xpgOBIDBEO5d2hcIRICRfVR6Mx0pgN3ymue8C+AGl0e16YyWWyOfsuUMZnKLMKFql8AA3KAZBT5DD4fwOsCsgrsjlEMZKFQlGZFMglNaLOAYKgwFDwIj7JEQDIwZZneoYOOJJNA+B7XNkOQAPgJGH2sRUovgDfgAAYHhbxr78Hh4CAAB5QVjiWKCj0CT38IA

💻 Code

declare const ValidTag: unique symbol;
declare type Tag = string & { [ValidTag]: true };

class User {
  public readonly handle: string;
  protected _tags: Tag[];

  constructor (
    user: User,
    newTags?: string[],
  ) {
    (
      {
        handle: this.handle,
        _tags: this._tags, // ERROR
      } = typeof newTags === "undefined"
        ? user
        : {
            handle: user.handle,
            _tags: User.checkTags(...newTags),
          }
    )

    this.handle = user.handle;
    this._tags = typeof newTags === "undefined"
      ? user._tags // OK
      : User.checkTags(...newTags);
  }

  private static checkTags(...T: string[]): Tag[] {
    return T.filter(
      (t: string): t is Tag =>
        t.length > 0, // trivial example
    );
  }
}

🙁 Actual behavior

TypeScript compiler fails to transpile this code to JavaScript, throwing this error on line 15 of the sample code (labeled Case 1):

Property '_tags' does not exist on type 'User | { handle: string; _tags: Tag[]; }'.
  • Type System: This is counterintuitive, because the logic in Case 2 looks equivalent to the untrained eye (there may be caveats I’m unaware of), but TypeScript does not error in that case.
  • Runtime: Although irrelevant to TypeScript desired behavior (as readonly is solely a type decoration), ES6 JavaScript equivalent syntax runs as expected:
class Foo {
  a; b;
  constructor(foo, newB) {
    ({ a: this.a, b: this.b } = typeof newB === "undefined"
      ? foo
      : { a: foo.a, b: Foo.processB(newB) });
  }
  static processB(B) { return B }
}
let me = new Foo({ a: 1, b: 2 });
console.log(me);                // {a:1, b:2}
console.log(new Foo(me));       // {a:1, b:2}
console.log(new Foo(me, 9001)); // {a:1, b:9001}

🙂 Expected behavior

TypeScript compiler successfully transpiles this code to JavaScript.

Alternative A: TypeScript compiler provides a friendlier error message to help the user fail soft (which can also potentially be Quick Fixed by VSCode).

Alternative B: Won’t Fix as expected behavior, wherein the closed bug may serve as a handy reference for any future searchers on this topic.

Additional information about the issue

No response

@whzx5byb
Copy link

Essentially duplicate of #9974.

@jimmy-zhening-luo
Copy link
Author

jimmy-zhening-luo commented May 14, 2024

Essentially duplicate of #9974.

What the… I am bad at searching. 😓 I will confirm in my example and then close as dupe.

I am expecting to see the following behavior if I declare a two interfaces A, B that mimic class UserA with protected _tags, and B undecorated or with public _tags:

  • new User() expects A | User: ✅
  • new User() expects B | User: ❌

Does that sound right?

EDIT: Never mind. I think that’s nonsensical on my part; if I understand the original bug correctly, the problem would be that the (undecorated) destructuring assignment call is implicitly public, therefore inherently unable to destructure a type with the same properties at a different privacy level, correct?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants