skip to Main Content

Tried to pass this with type in a class method and then try to access it in 2 ways:

  1. Create a new JS object and assign a value. (No Error)

    {getName: obj1.getName} in below code

  2. Save it to a new variable(reference) and call it (Gives Error)

    const a = obj1.getName; in below code

What is the concept behind these two? When I have defined this: MyClass saying it is of type MyClass then it should give error for the first one as well.

typescript code

class MyClass {
    x:string = "Class value";
    
    getName(this: MyClass) {
        return this.x;
    }
}

// Creating class object
const obj1 = new MyClass();
console.log(obj1.getName());

// Creating JS object
const obj2 = {x: 'Object Value', getName: obj1.getName}

// Will not give error
console.log(obj2.getName());

// Will give error with below line
const a = obj1.getName;
a(); 

Error: The 'this' context of type 'void' is not assignable to method's 'this' of type 'MyClass'.(2684)

Ref:

3

Answers


  1. Chosen as BEST ANSWER

    In the above code obj2 has same signature/definition as MyClass thats why it consider that object as an instance of class MyClass and hence didn't give error.

    While in second case it has different signature and hence typescript gave error.

    If we try to create obj2 with x as a number, then signature gets different and it will give error:

    Example:

    const obj2 = {x: 123, getName: obj1.getName}
    

    Error: The 'this' context of type '{ x: number; getName: (this: MyClass) => string; }' is not assignable to method's 'this' of type 'MyClass'

    Another example:

    class MyClass {
        x:string = "Class value";
        
        getName(this: MyClass) {
            // Now returning a string directly removing reference.
            return 'Hello';
        }
    }
    const obj1 = new MyClass();
    console.log(obj1.getName());
    
    // Creating JS object
    const obj2 = {x: 3, getName: obj1.getName}
    
    // Will now give error
    console.log(obj2.getName());
    

    Error:

    The 'this' context of type '{ x: number; getName: (this: MyClass) => string; }' is not assignable to method's 'this' of type 'MyClass'.
      Types of property 'x' are incompatible.
        Type 'number' is not assignable to type 'string'.(2684)
    

  2. If this were just regular JS I would advise you to read more about bind, call and apply and how context can be re-used/re-created.

    I think (based on my limited experience) that this time the ts error is spot on. The ‘a’ variable does not have any context of what this.x is. This is why ts is smartly giving you an error. Upon invocation, there is no way for it to determine where/what this.x is.

    Login or Signup to reply.
  3. The error you encountered is due to a type mismatch between the expected ‘this’ context of the ‘getName’ method and the actual context provided when invoking the method through the ‘a’ variable.

    In the MyClass definition, the getName method is explicitly annotated with this: MyClass, indicating that it expects to be called on an instance of MyClass. This ensures that the method can access the x property correctly.

    However, when you assign obj1.getName to a and invoke it as a(), the ‘this’ context is no longer implicitly bound to obj1. Therefore, TypeScript infers the type of ‘a’ as (this: void) => string, which means it is a function expecting no context (or a context of void). Consequently, TypeScript raises an error when you invoke a() because it does not meet the expected ‘this’ context type for getName.

    To fix this issue, you can explicitly bind the ‘this’ context to obj1 when assigning obj1.getName to a. Here’s an updated code snippet:

    const a = obj1.getName.bind(obj1);
    console.log(a()); // Output: "Class value"
    By using the bind method, you ensure that the ‘this’ context inside a is bound to obj1, allowing the method to be invoked successfully without any type errors.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search