0

Have been working on adding an abstract class to support versioning for Resolvers for a floorPlan feature and was wondering how I might fix this error. At the moment I do not get any Type errors but when the code compiles NestJs throws this error:

Undefined type error. Make sure you are providing an explicit type for the "floorPlanUpdate" (parameter at index [2]) of the "BaseResolverHost" class.

In a normal resolver this is not a problem as you pass in the type e.g.:

    @Args('input') input: UpdateFloorPlanV1Input,

However as I am trying to determine the input type based off the version of the Floorplan using this line:

      @Args('input') input: UpdateFloorPlanVersion<FloorPlanVersion>,

So it is not possible to explicitly declare it here. Let me know if this is possible or if anyone has any other approaches

This is the abstract Resolver code:

import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import { UserId } from 'src/users/decorators/user-id.decorator';
import { CreateFloorPlanInput } from './v1/dto/create-floor-plan.input';
import { UpdateFloorPlanV1Input } from './v1/dto/update-floor-plan.input';
import { FloorPlanV1 } from './v1/models/floor-plan.model';
import { UpdateFloorPlanV2Input } from './v2/dto/update-floor-plan.input';
import { FloorPlanV2 } from './v2/models/floor-plan.model';

export type FloorPlanVersion = FloorPlanV1 | FloorPlanV2;

export type UpdateFloorPlanVersion<T extends FloorPlanVersion> =
  T extends FloorPlanV1 ? UpdateFloorPlanV1Input : UpdateFloorPlanV2Input;

export interface FloorPlanServiceInterface<FloorPlanVersion> {
  create(
    userId: string,
    createFloorPlanInput: CreateFloorPlanInput,
  ): Promise<FloorPlanVersion>;
  findOne(userId: string, floorPlanId: string): Promise<FloorPlanVersion>;
  update(
    userId: string,
    floorPlanId: string,
    input: UpdateFloorPlanV1Input,
  ): Promise<FloorPlanVersion>;
}

export function BaseResolver<T extends Type<FloorPlanVersion>>(
  classRef: T,
  version: string,
) {
  @Resolver({ isAbstract: true })
  abstract class BaseResolverHost {
    floorPlanService: FloorPlanServiceInterface<FloorPlanVersion>;
    constructor(readonly service: FloorPlanServiceInterface<FloorPlanVersion>) {
      this.floorPlanService = service;
    }

    @Mutation(() => classRef, { name: `floorPlanCreate${version}` })
    async floorPlanCreate(
      @UserId() userId,
      @Args('input')
      input: CreateFloorPlanInput,
    ) {
      return this.floorPlanService.create(userId, input);
    }

    @Query(() => classRef, { name: `floorPlan${version}` })
    async floorPlan(@UserId() userId, @Args('id') id: string) {
      return this.floorPlanService.findOne(userId, id);
    }

    @Mutation(() => classRef, { name: `floorPlanUpdate${version}` })
    async floorPlanUpdate(
      @UserId() userId,
      @Args('id') id: string,
      @Args('input') input: UpdateFloorPlanVersion<FloorPlanVersion>,
    ) {
      return this.floorPlanService.update(userId, id, input);
    }
  }
  return BaseResolverHost;
}

And then it is called like this:

@Resolver(() => FloorPlanV2)
export class FloorPlanV2Resolver extends BaseResolver(FloorPlanV2, 'V2') {
  constructor(
    readonly floorPlanService: FloorPlanService,
  ) {
    super(floorPlanService);
  }
}

So if the ObjectType was FloorPlanV2 we would expect the UpdateFloorPlanInputType to be UpdateFloorPlanV2Input

Christian Bell
  • 314
  • 2
  • 11

0 Answers0