Dealing with asynchronous constructors in Typescript

Guillaume Viguier-Just
2 min readOct 2, 2020

--

When you create a class in Typescript, you might need to perform some asynchronous operations in the constructor. For example, if you are creating a class to encapsulate some calls to a specific API, you might want to, in the constructor, authenticate to the API to get a fresh token that you will then be able to use in your API calls. Writing something like this:

async constructor (args) {
this.apiInstance = new Api(args);
await this.apiInstance.authenticate();
}

is very tempting, unfortunately, at the time I’m writing this article, asynchronous constructors are not supported in Typescript. They might become supported by the language in a future version, but meanwhile, how can we deal with async operations in the constructor of a class ?

Using an asynchronous init method

One way is to use an asynchronous init method, which will need to be called by the client. Something along these lines:

class MyApi {
private apiInstance
constructor (args) {
// Do some synchronous operations if needed
this.apiInstance = new ApiInstance(args);
}
async init () {
await this.apiInstance.authenticate();
// Do some more asynchronous operations if needed
}
}

This class will then need to be used in the following way:

const api = new MyApi(args);
await api.init();

This works, however it requires the client to call the init method right after it creates an instance of the class, which means 2 lines of code instead of one.

Using a factory method

To overcome this issue, you can add a factory static method to your class, which will return an instance of the class where the init function has already been called:

class MyApi {
private apiInstance
constructor (args) {
// Do some synchronous operations if needed
this.apiInstance = new ApiInstance(args);
}
async init () {
await this.apiInstance.authenticate();
// Do some more asynchronous operations if needed
}
async static build (args) {
const api = new MyApi(args);
await api.init();
return api;
}
}

This is how a client would use the MyApi class:

const api = await MyApi.build(args);

Conclusion: use an asynchronous init method AND provide a factory method

Doing BOTH: providing an init method AND a factory static method is definitely what I would recommend, as it gives you the best of both worlds. Client implementations can use the short version, ie:

const api = await MyApi.build(args);

but, if for some reason they need to call the constructor and the init method independently, they can still do so, bypassing your factory method. In case you want to forbid clients to use the constructor, and force them to go through your factory method, you can also declare the constructor as private.

--

--