Skip to content

Private field check narrows generic class too far #46668

Open
@molisani

Description

@molisani

Bug Report

Discovered a potential issue with #44648 in regards to how classes with type parameters are handled. The presence of the brand indicates that the object is an instance of this class, but doesn't say anything about the type arguments. For instanceof the current behavior is to pass any for all type arguments (see #17473 for that issue) but the default here should be that the type matches the constraint for that corresponding type parameter.

🔎 Search Terms

ts4.5-beta, private fields, brand checks, generic, type parameter, narrowing

🕗 Version & Regression Information

TypeScript Version: ts4.5-beta

  • I was unable to test this on prior versions because the feature didn't exist yet.

⏯ Playground Link

Playground link with relevant code

💻 Code

type Constraint = { foo: string } | { bar: number };

class MyClass<T extends Constraint> {
    #brand = true;
    value: T;
    constructor(value: T) {
        this.value = value;
    }

    copyValueFrom(x: object) {
        if (#brand in x) {
            this.value = x.value; 
        }
    }
}

const wrappedFoo = new MyClass({ foo: "foo" });
const wrappedBar = new MyClass({ bar: 123 });
wrappedFoo.copyValueFrom(wrappedBar);
console.log(wrappedFoo.value.foo); // type expects foo, value is bar

🙁 Actual behavior

Assignment in copyValueFrom proceeded without error, results in mismatch between type and value.

🙂 Expected behavior

In copyValueFrom, expected brand to narrow x to MyClass<Constraint> and report error Type 'Constraint' is not assignable to type 'T'. when assigning this.value = x.value.

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptFix AvailableA PR has been opened for this issueRescheduledThis issue was previously scheduled to an earlier milestone

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions