Home > Blockchain >  Type error implementing a map for objects
Type error implementing a map for objects

Time:01-06

I'm trying to implement a helper map for objects that looks like this:

import { objectEntries, objectFromEntries } from 'ts-extras';

export const map = <K extends PropertyKey, V>(
    obj: Record<K, V>,
    fn: (key: K, value: V, index?: number) => [K, V]
) => {
    const entries = objectEntries(obj);
    const mappedEntries = entries.map(([key, value], index) => fn(key, value, index));
    return objectFromEntries(mappedEntries);
};

But I'm getting the following error in argument key of fn call inside entries.map callback.

Argument of type '`${Exclude<K, symbol>}`' is not assignable to parameter of type 'K'.
  '`${Exclude<K, symbol>}`' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'PropertyKey'.
    Type 'string' is not assignable to type 'K'.
      'string' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'PropertyKey'

I would really appreciate any ideas on how to solve this error. Thanks in advance!

CodePudding user response:

The type definition of objectEntries from ts-extras was written in a way to closely reflect the behavior of Object.entries(). It knows that symbol properties do not appear in the result set and filters them out with Exclude<K, symbol>. It also knows that all keys returned from the functions are strings, so it wraps the type in a template literal type which converts number to string and number literals to string literals.

This results in the type `${Exclude<K, symbol>}`. While this type is somewhat related to K, the compiler correctly denies the assignment.

Changing the type of key to `${Exclude<K, symbol>}` to reflect the typing of objectEntries should solve the issue.

export const map = <K extends PropertyKey, V>(
    obj: Record<K, V>,
    fn: (key: `${Exclude<K, symbol>}`, value: V, index?: number) => [K, V]
) => {
    const entries = objectEntries(obj);
    const mappedEntries = entries.map(
      ([key, value], index) => fn(key, value, index)
    );
    return objectFromEntries(mappedEntries);
};

Playground

  • Related