Skip to main content

prefer-ts-extras-object-map-values

Prefer objectMapValues from ts-extras over key-preserving objectFromEntries(objectEntries(...).map(...)) chains.

This rule lives only in the experimental preset and reports without autofixing.

Targeted pattern scopeโ€‹

This rule focuses on ts-extras entry pipelines whose callback preserves the original key and only changes the value.

  • objectFromEntries(objectEntries(value).map(([key, item]) => [key, nextValue]))
  • the same shape with a block body that immediately returns [key, nextValue]

It intentionally skips broader patterns that can encode different intent.

What this rule reportsโ€‹

This rule reports ts-extras object remapping chains when all of the following are true:

  • the outer call is objectFromEntries(...)
  • the inner source is objectEntries(...)
  • the intermediate step is a direct .map(...) call
  • the callback is an arrow function with a strict [key, value] tuple parameter
  • the callback returns a two-item tuple whose first item is the original key

The rule is currently report-only. It does not autofix or suggest a replacement yet.

Why this rule existsโ€‹

objectMapValues reduces noise in a common value-only remapping pattern.

  • The callback contract matches the intent directly: (value, key) => nextValue.
  • Readers do not need to mentally unpack an entries pipeline to see that keys stay the same.
  • The code avoids a temporary array of tuples in authored source.

Because this pattern appears in semantically different forms, the rule stays narrow on purpose and reports only equivalent key-preserving remaps.

โŒ Incorrectโ€‹

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

const statusById = {
alpha: "up",
beta: "down",
} as const;

const labels = objectFromEntries(
objectEntries(statusById).map(([key, value]) => [key, `${key}:${value}`])
);

โœ… Correctโ€‹

import {objectMapValues} from "ts-extras";

const statusById = {
alpha: "up",
beta: "down",
} as const;

const labels = objectMapValues(
statusById,
(value, key) => `${key}:${value}`
);

Behavior and migration notesโ€‹

  • This rule only reports chains that preserve the original key unchanged.
  • It ignores native Object.entries / Object.fromEntries pipelines to avoid overlapping with the stable objectEntries and objectFromEntries adoption rules.
  • It ignores .map(...) calls that pass a thisArg, because objectMapValues does not have an equivalent callback binding parameter.
  • It ignores function-expression callbacks for now, because the experimental rule avoids assuming this behavior is irrelevant.

Additional examplesโ€‹

โŒ Incorrect โ€” Additional exampleโ€‹

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

const statusById = {
alpha: "up",
beta: "down",
} as const;

const uppercased = objectFromEntries(
objectEntries(statusById).map(([key, value]) => [key, value.toUpperCase()])
);

โœ… Correct โ€” Additional exampleโ€‹

import {objectMapValues} from "ts-extras";

const statusById = {
alpha: "up",
beta: "down",
} as const;

const uppercased = objectMapValues(statusById, (value) => value.toUpperCase());

ESLint flat config exampleโ€‹

import typefest from "eslint-plugin-typefest";

export default [typefest.configs.experimental];

When not to use itโ€‹

Disable this rule if your team prefers explicit entry-tuple pipelines for readability, or if the remapping logic changes keys often enough that objectMapValues is not the dominant style.

Package documentationโ€‹

ts-extras package documentation:

Source file: source/object-map-values.ts

import {objectMapValues} from "ts-extras";

const object = {a: 1, b: 2, c: 3};

const mapped = objectMapValues(object, value => String(value));
//=> {a?: string; b?: string; c?: string}

Rule catalog ID: R086

Further readingโ€‹

Adoption resourcesโ€‹