0
votes

If you want to run this in the browser, I create a Typescript Playground

I had similar errors before which I was able to fix, but this one just does not seem to want to go away. I am sure it's a typing problem, but no clue on how to go about this.

I looked up similar situations which all advocated you'd properly type the object you're working on, which'd be task in my case, which I casted to be of type TaskInterface. I typed the param of replace.key as keyof TaskInterface. Not sure what more I can do.

Can the optional properties in the interface be the cause of it saying typeof never?

interfaces

export interface PopulatableTaskInterface {
  columns?: unknown;
}
export interface TaskBase extends PopulatableTaskInterface {
  title: string;
  description?: string;
  participants?: Array<string>;
  deadline?: Date;
  taskIdentifier?: number;
  messageIds?: Types.Map<string>;
  guildId: mongoose.Types.ObjectId;
  completed?: boolean;
  createdTimestamp?: number;
  completedTimestamp?: number;
  wip?: boolean;
}
export interface TaskBaseInterface extends TaskBase, mongoose.Document {}

export interface TaskInterface extends TaskBaseInterface {
  columns: Types.Array<Schema.Types.ObjectId>;
}

Function

async edit(
    query: {
      key: keyof TaskBase;
      value: unknown;
    },
    replace: { key: keyof TaskInterface; value: unknown }
  ): Promise<TaskInterface> {
    const { key, value } = query;
    try {
      const task = (await TaskModel.findOne({ [key]: value })) as TaskInterface;
      if (!task) {
        throwError(
          `Insufficient query: ${query}`,
          Errors.insufficientQuery,
          __dirname,
          __filename
        );
      }
      task[replace.key] = replace.value; // This is where I get the error

      task.markModified(replace.key);
      task.save();
      return task;
    } catch (err) {
      console.log(err);
    }
  }
}

EDIT: changed replace param to this

edit(query:{...}, replace: {key: keyof TaskBase, value: unknown}):
  Promise<TaskInterface> {
    // code here
    task[replace.key] = replace.value; // replaced replace.key for each key key manually
    // by doing `task["taskIdentifier"] = replace.value`, etc.
    // which DID NOT throw an error
}

now it's throwing the following error: Type 'unknown' cannot be used as an index type.ts(2538)

2

2 Answers

0
votes

unknown is just like any, but inferred its type when type checking. So unknown must not be assigned as a value of TaskInterface. Then, need to type value like following:

async function edit<K extends keyof TaskInterface>(
    query: {
      key: keyof TaskBase;
      value: TaskBaseInterface[K];
    },
    replace: { key: keyof TaskBase; value: TaskBaseInterface[K] }
  ): Promise<TaskInterface | void> {
    const { key, value } = query;
   ...

It's not necessary to use generics, though I use to make the code easy to read.

0
votes

Extending on Akihito's answer, this could work for you:

async function edit<K extends keyof TaskInterface>(
  query: { key: keyof TaskBase; value: unknown; },
  replace: { key: K; value: TaskInterface[K]; }
): Promise<TaskInterface | void> {
  // ...
}

This way you would make sure replace object's key and value refer to the same property, so the error should disappear.