I've created a wrapper class for S3 operations but when I compile using the TypeScript complier I get the following error:
lib/Store.ts:20:15 - error TS2531: Object is possibly 'null'.
20 await this.store.upload({
I'm new to TypeScript but I understand TypeScript is doing it's job here and preventing the possibility that await this.store.upload()
could run while this.store
is null
. What's the correct way for dealing with this type of situation where a class value might not yet be initialised?
My wrapper class:
import S3 from 'aws-sdk/clients/s3';
export class Store {
storeName: string;
store: S3 | null;
initialised: boolean;
constructor(storeName: string) {
this.storeName = storeName;
this.store = null;
this.initialised = false;
}
_init(): void {
this.store = new S3();
}
async _writeToStore(data: object, path: string): Promise<void> {
if (!this.initialised) this._init();
await this.store.upload({
Bucket: this.storeName,
Key: path,
Body: data
}).promise();
}
}
I've always tried to avoid creating new instances of classes in the constructor because it's awkward to mock. Maybe passing a new class instance into the constructor is the best approach?
Typescript is giving you that error because you have strictNullChecks
enabled and your property store
has null
as a possible type.
You can do any of these options
null
typeYou can probably drop the null
type on store
, since you are setting the value of that property on your constructor, and nothing on your code sets it to null
:
store: S3;
Alternatively, if this.store
is never going to be null when you execute this.store.upload({...})
, then you can add a non-null assertion operator (!) like this:
this.store!.upload({...})
That will tell Typescript to stop giving the error. Note that this doesn’t change the runtime behavior of your code, so it's important to only use !
when you know that the value can't be null
or undefined
.
store
for null
value right beforeYou can explicitly check this.store
for null value right before calling this.store.upload()
. But this call must be done within the same method, like this:
if (!this.store) {
this.store = new S3();
}
await this.store.upload({...});
This won't work:
if (!this.store) {
this._init();
}
await this.store.upload({...});
Personally, I would go with option 1. I'm assuming that your reason for writing a wrapper for S3
is so that your consumers never have to instantiate and work directly with an S3
object and instead instantiate and work with your wrapper class/object.