Type error when using Logical or with Typescript 4

109 views Asked by At

I have a class property that is constructed with an initial value:

private _httpClient: ClientInterface = new HttpURLConnectionClient();

which I want to override if it's passed when instantiating the class:

class Class {
  private _httpClient: ClientInterface = new HttpURLConnectionClient();

  constructor(httpClient?: ClientInterface) {
    if (httpClient) {
      this._httpClient = httpClient // replaces existing _httpClient
    }
  }
}

new Class(httpClient)

I'm migrating this code to TypeScript 4, and I assumed the following would be equivalent:

class Class {
  private _httpClient: ClientInterface = new HttpURLConnectionClient();

  constructor(httpClient?: ClientInterface) {
      this._httpClient ||= httpClient // Shows ts error! But should be equivalent to this._httpClient = httpClient || this._httpClient
  }
}

new Class(httpClient)

but it displays Type 'X | undefined' is not assignable to Type 'X', which is understandable if I was not using the logical or.

I know I can solve this issue if I do this, but I want to understand why the solution above shows a type error:

class Class {
  private _httpClient: ClientInterface;

  constructor(httpClient?: ClientInterface) {
      this._httpClient = httpClient || new HttpURLConnectionClient()
  }
}

new Class(httpClient)
1

There are 1 answers

0
jonrsharpe On BEST ANSWER

You have slightly misinterpreted what short-circuiting assignment is doing. a ||= b is equivalent to a = a || b, it deals with the left-hand side of the assignment possibly being undefined, not the right-hand side.

In your case, that's something like:

this._httpClient = this._httpClient || httpClient

(The compiler actually emits this._httpClient || (this._httpClient = httpClient), but it's easier to think about this way.)

Your latter version is the other way around (a = b || a):

this._httpClient = httpClient || new HttpURLConnectionClient()
                              // ^ equivalent to this._httpClient

Probably the easiest way to implement this would be to use a parameter property with a default value:

class Class {
  constructor(private _httpClient: ClientInterface = new HttpURLConnectionClient()) { }
}