Skip to content
Skilled.dev

Free lesson walkthrough · TypeScript

Fix this and closure bugs in TypeScript

Fix receiver and closure bugs in TypeScript. Bind a presenter callback, preserve per-iteration seat positions, and test what changes when captured objects mutate.

4 min read · Worked code, edge cases, and complexity

The meeting example contains two separate bugs. getPresenterName should read the first user's name, and each user's printSeatNumber callback should print that user's index. The exercise limits changes to the Meeting class and asks for minimal refactoring. Those constraints let you fix the causes without redesigning the entire example.

Preserve the method's intended receiver

Assigning a user's getName method to a property on the meeting copies a function reference. It does not automatically retain the original user as the function's receiver. Calling that function as a meeting method supplies the meeting as this, so a lookup intended for the user's name reads the wrong object.

Binding the function to the presenter creates a callable that retains that intended receiver. A wrapper that explicitly calls the user's method can also preserve it, but the lesson uses a small targeted fix. Trace the actual call expression before applying bind everywhere: arrow functions have lexical this, and different call forms follow different receiver rules.

Give each callback its own loop binding

The seat callbacks are created in a loop using var. They close over the same function-scoped index binding. By the time they execute, the loop has advanced that binding to its terminating value, so every callback prints the same out-of-range index.

Using let in the loop declaration gives the callbacks per-iteration bindings. An immediately invoked function with an index argument can create a separate binding too, but requires more code. The fix concerns which binding each callback reads later; it is not a general rule that closures copy every value at creation time.

Test the failures independently

First test the presenter result while leaving the seat callbacks alone. Then invoke every seat callback after the loop has finished and expect the sequence of original indexes. Calling callbacks immediately inside the loop would hide the lifetime issue and would not reproduce the reported failure.

Open the current lesson for the original input and complete changes. The JavaScript preparation guide connects receiver and closure behavior with further practice. When explaining your fix, name the object used as this and the exact variable binding retained by each callback; those two facts account for the observed output.

Repair two independent callback failures

The example binds a presenter's regular method before returning it as a callback. Calling that saved function later still reads the intended presenter. Because the bound function retains the object rather than copying its name, changing the presenter's name before invocation changes the result it reads.

The seating callbacks use a let binding for each loop iteration. With names Lin and Grace, invoking them later gives Seat 1: Lin and Seat 2: Grace. A single function-scoped var index would instead be read after the loop has advanced beyond the final valid position.

Check lifetime as well as the first result

Binding creates a new function identity and retains its receiver. If a subscription API requires that exact function for removal, save the bound function instead of binding again during cleanup. The second bind call would create another function object.

Creating n seating callbacks takes O(n) time and O(n) returned function storage. The callbacks retain access to the names array and their position bindings; they do not clone every name. Treat the input as unchanged while these callbacks are in use, or explicitly copy it if snapshot behavior is needed. Check no names, two names, a changed presenter name, and invocation after the construction function has returned. These cases verify both binding decisions without relying on arbitrary timer delays.

TypeScript example and implementation choices

The Presenter parameter property declares and initializes its name field, and readPresenter inherits the bound method's string result. The readonly names annotation restricts mutations through this parameter, while it does not freeze a caller's array at runtime. Each seat callback is typed as () => string. These annotations describe the repair; bind and the let loop perform the actual runtime work.

Worked example

class Presenter {
  constructor(public name: string) {}
  getName(): string { return this.name; }
}

function meetingCallbacks(presenter: Presenter, names: readonly string[]) {
  const readPresenter = presenter.getName.bind(presenter);
  const seats: Array<() => string> = [];
  for (let index = 0; index < names.length; index++) {
    seats.push(() => 'Seat ' + (index + 1) + ': ' + names[index]);
  }
  return { readPresenter, seats };
}

Continue in TypeScript

Open Fix 'this' for the complete course explanation. The course keeps your saved programming-language choice. Use this standalone page to inspect the TypeScript example and run it in your own local environment. Follow the original course sequence when you want the surrounding lesson context and related exercises.

The TypeScript interview preparation guide connects these language choices with further practice and the cross-language operation reference.

Continue with the lesson

Open the original lesson to explore its full explanation and examples. Your saved programming-language choice stays unchanged. This reading page belongs to that lesson and does not create a separate progress record.