Home > database >  Property is missing in type but required in type when using Object.defineProperty
Property is missing in type but required in type when using Object.defineProperty

Time:12-17

I have this validation middleware that I'm trying to write to validate an empty payload when sending requests :

const _isMissing = (body: any): boolean => !body;

export const isMissing = Object.defineProperty(_isMissing, '_apiGatewayResponse', {
  value: LAMBDA_400_RESPONSE,
});

interface ValidationFunction extends Function {
  _apiGatewayResponse: APIGatewayProxyResult;
}

export function validateRequestBody(
  handler: LambdaFunction,
  validations: Array<ValidationFunction>,
): LambdaFunction {
  const wrapper: LambdaFunction = async (
    event: APIGatewayEvent,
  ): Promise<APIGatewayProxyResult> => {
    const eventBody = event.body ? JSON.parse(event.body) : null;
    for (const validation of validations) {
      const invalidBody = validation(eventBody);
      if (invalidBody) {
        return validation._apiGatewayResponse || LAMBDA_400_RESPONSE;
      }
    }

    const response = await handler(event);
    return response;
  };

  return wrapper;
}

But when I come to use the middleware function :

validateRequestBody(myPostFunction, [isMissing]);

I get a TypeScript error on isMissing stating

Property '_apiGatewayResponse' is missing in type '(body: any) => boolean' but required in type 'ValidationFunction'.

Can anyone help me resolve this? I couldn't really find any similar problems and would appreciate any help.

Thank you!

CodePudding user response:

The type of isMissing (which comes from _isMissing) is (body: any) => boolean (a function accepting a single parameter of type any and returning a boolean), but ValidationFunction requires an _apiGatewayResponse property on it. Even though you add the property at runtime via Object.defineProperty, that doesn't change the compile-time type that the function has.

You can assure TypeScript that the result has the right type via an innocuous type assertion that only asserts what you can easily see the code is adding. For example:

type IsMissingFunction = ((body: any) => boolean) & ValidationFunction;

const _isMissing = (body: any): boolean => !body;

export const isMissing: IsMissingFunction = Object.defineProperty(
    _isMissing as typeof _isMissing & Pick<ValidationFunction, "_apiGatewayResponse">,
    '_apiGatewayResponse', {
        value: LAMBDA_400_RESPONSE
    }
);

Playground link

You could just use as IsMissingFunction instead of the more long-winded as typeof _isMissing & Pick<ValidationFunction, "_apiGatewayResponse">:

type IsMissingFunction = ((body: any) => boolean) & ValidationFunction;

const _isMissing = (body: any): boolean => !body;

export const isMissing: IsMissingFunction = Object.defineProperty(
    _isMissing as IsMissingFunction,
    '_apiGatewayResponse', {
        value: LAMBDA_400_RESPONSE
    }
);

The reason I prefer the first version is that if you edit IsMissingFunction (or ValidationFunction) later to add more properties, TypeScript will raise a helpful error on the first example indicating that the function no longer fits the definition of IsMissingFunction. With the second example, you'd have to make a really big change for it to complain.

  • Related