2

I am currently working on a personal project to provide easy access to different storage providers through a NestJS package. I want the package to be fully type-safe but I am experiencing difficulty achieving this.

This is an abstraction of the current code.

// local.driver.ts

interface LocalOptions {
  path: string;
}

export class LocalDriver {
  constructor(private options: LocalOptions) {}

  get() {}
}
// s3.driver.ts

interface S3Options {
  key: string;
  secret: string;
  region: string;
  bucket: string;
}

export class S3Driver {
  constructor(private options: S3Options) {}

  get() {}
}
// storage.service.ts

const drivers = {
  local: LocalDriver,
  s3: S3Driver,
};

type DriverTypes = keyof typeof drivers;
type Drivers = typeof drivers[DriverTypes];

type Disk = {
  [Driver in DriverTypes]: {
    type: Driver;
    properties: ConstructorParameters<typeof drivers[Driver]>[0];
  };
}[DriverTypes];

export type StorageOptions = {
  disks: {
    [disk: string]: Disk;
  };
};

@Injectable()
export class StorageService {
  private static diskDrivers: { [disk: string]: Drivers };
  private static options: StorageOptions;

  constructor(@Inject(STORAGE_OPTIONS) options: StorageOptions) {
    StorageService.options = options;
  }

  static disk(disk: keyof typeof StorageService.options.disks) {
    return StorageService.getDriver(disk);
  }

  private static getDriver(
    disk: keyof typeof StorageService.options.disks,
  ): Drivers {
    const diskDriver = StorageService.diskDrivers[disk];
    if (diskDriver) return diskDriver;
    const driver = StorageService.newDriver(disk);
    StorageService.diskDrivers[disk] = driver;
    return driver;
  }

  private static newDriver(
    disk: keyof typeof StorageService.options.disks,
  ): Drivers {
    const driverConfig = StorageService.options.disks[disk];
    const Driver = drivers[driverConfig.type];
    return new Driver(driverConfig.properties);
                                    // ^ Argument of type 'LocalOptions | S3Options' is not assignable to parameter of type 'LocalOptions & S3Options'.
  }
}

This is how I register the package in NestJS. When using a specific type, only its options are allowed and will be suggested. E.g. using type s3, only the S3 options are allowed in the IDE (this is currently working).

// app.module.ts

@Module({
  imports: [
    StorageModule.register({
      disks: {
        invoices: {
          type: 's3',
          properties: {
            region: process.env.AWS_REGION,
            key: process.env.AWS_KEY,
            secret: process.env.AWS_SECRET,
            bucket: 'invoices',
          },
        },
        images: {
          type: 'local',
          properties: {
            path: '/images',
          },
        },
      },
    }),
  ],
})
export class AppModule {}

I now want to use the disks but I'm not receiving any suggestions, warnings or errors.

StorageService.disk('').get();
                  // ^ This should suggest and only allow `invoices` and `images`.

Is there a way to achieve this functionality?

samsepi
  • 41
  • 4

0 Answers0