How do i type this payload object update?

Viewed 40

so i'm working on a route and wanted to create a generic normalizer to use before saving my user into the database.

This is my normalizer function

import { INormalizer, IPayloadIndexer } from "../../interfaces/generics";

const normalizeRequestValues = <Type>(
  payload: IPayloadIndexer<Type>,
  normalizer: INormalizer
) => {
  const normalizerKeys = Object.keys(normalizer);

  const payloadEntries = Object.entries(payload);

  payloadEntries.forEach(([key, value]) => {
    if (normalizerKeys.includes(key)) {
      payload[key] = normalizer[key](value as string);
    }
  });
};

export default normalizeRequestValues;

And on the payload[key] = normalizer[key](value as string); part of the code i'm receiving this error:

(parameter) payload: IPayloadIndexer<Type>
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'IPayloadIndexer<Type>'.
  No index signature with a parameter of type 'string' was found on type 'IPayloadIndexer<Type>'

INormalizer:

type INormalizer = {
  [key: string]: (str: string) => string;
};

export default INormalizer;

IPayloadIndexer:

type IPayloadIndexer<Type> = {
  [Property in keyof Type]: Type[Property];
};

export default IPayloadIndexer;

The function is being called first here:

import { IPayloadIndexer } from "../../interfaces/generics";
import { normalizeRequestValues } from "../generics";

import userNormalizer from "./userNormalizer.utils";

const normalizeUserRequestValues = <Type>(payload: IPayloadIndexer<Type>) => {
  normalizeRequestValues<Type>(payload, userNormalizer);
};

export default normalizeUserRequestValues;

Using this normalizer:

import { INormalizer } from "../../interfaces/generics";
import { capitalizeText } from "../generics";

const userNormalizer: INormalizer = {
  name: (name: string) => capitalizeText(name),
  email: (email: string) => email.toLowerCase(),
};

export default userNormalizer;

And then it is called in my route service, here:

import { hash } from "bcrypt";
import { Request } from "express";

import { IServiceReturnMessage } from "../interfaces/generics";
import { IUser, IUserCreate } from "../interfaces/users";
import { UserRepository } from "../repositories";
import { normalizeUserRequestValues, serializeUser } from "../utils/user";

class UserService {
  insertUser = async ({
    validated,
  }: Request): Promise<IServiceReturnMessage<IUser>> => {
    validated.password = await hash(validated.password, 10);

    normalizeUserRequestValues<IUserCreate>(validated as IUserCreate);

    const user = await UserRepository.save({ ...validated });

    const serializedUser = serializeUser(user);

    return { statusCode: 201, message: serializedUser };
  };
}

export default new UserService();

I could make it work by passing a //@ts-ignore above the line, but i really wanna learn how to do it, if any extra information is needeed i will gladly post in here.

Thanks in advance for any response!

1 Answers

I think that IPayloadIndexer<Type> is useless in your case, you've defined each type of payload (like IUserCreate etc.), there's no need to define another type to abstract them. try to use Type directly.

const normalizeRequestValues = <Type>(
  payload: Type, // just use Type here
  normalizer: INormalizer
) => {
  ...
};

const normalizeUserRequestValues = <Type>(payload: Type) => { // just use Type here
  normalizeRequestValues<Type>(payload, userNormalizer);
};
Related