Skip to content

feat(eslint-plugin): [consistent-indexed-object-style] report mapped types #10160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

kirkwaiblinger
Copy link
Member

@kirkwaiblinger kirkwaiblinger commented Oct 16, 2024

PR Checklist

Overview

This reports on mapped types that can be converted to Records. However, note that not all mapped types can be converted to Records, so the rule only reports when they can be converted.

// can't be converted to record, because `K` is used in the computed type. Not reported.
type Foo1 = {
  [K in T]: K
};

// Can be converted to record. Reported.
type Foo2 = {
  [K in T]: T
}
// fixes to 
type Foo2 = Record<T, T>

// Can be converted to record. Reported.
type WithModifiers = {
  +readonly [key in T]+?: T
}
// fixes to 
type WithModifiers = Readonly<Partial<Record<T, T>>>


// WEIRD CASE: Cannot be converted to Record due to keyof (mapped type syntax preserves readonly modifiers in the keys of T)
type WithKeyof = {
  [K in keyof T]: T
}

// WEIRD CASE: Can be converted to Record due to keyof being parenthesized
type WithKeyof = {
  [K in (keyof T)]: T
}

For more detail on the "weird case", study the following example. (Thanks to @Retsam for the helpful discord conversation in https://discord.com/channels/508357248330760243/508357638677856287/1296364542498177046!)

type MyObject = {
  required: string,
  optional?: number,
}

type BooleanValuesNoParens = {
  [key in keyof MyObject]: boolean
}

// valid
const bv: BooleanValuesNoParens = {
  required: true,
}

type BooleanValuesWithParens = {
  [key in (keyof MyObject)]: boolean
}

// TS ERROR: Property 'optional' is missing
const bv: BooleanValuesWithParens = {
  required: true,
}

This actually has practical application inside our repo!

): {
// intentionally not using a Record to preserve optionals
[k in keyof ParseResult]: unknown;
};


There is one open question: should we report on cases where the -readonly modifier is used? I've gone with the approach for now that we should report it even though we can't provide a fix. Open to differing opinions on this, though.

type PlusReadonly = {
  +readonly [K in T]: T
}
// equivalent to 
type PlusReadonly = Readonly<Record<T, T>>

type PlusOptional = {
  [K in T]+?: T
}
// equivalent to
type PlusOptional = Partial<Record<T, T>>

type MinusOptional = {
  [K in T]-?: T
}
// equivalent to
type MinusOptional = Required<Record<T, T>>


type MinusReadonly = {
  -readonly [K in T]: T
}
// no built-in equivalent :( You can write your own easily though.
type Mutable<T> = {
  -readonly [K in keyof T]: T[K]
}
// then,
type MinusReadonly = Mutable<Record<T, T>>

@typescript-eslint
Copy link
Contributor

Thanks for the PR, @kirkwaiblinger!

typescript-eslint is a 100% community driven project, and we are incredibly grateful that you are contributing to that community.

The core maintainers work on this in their personal time, so please understand that it may not be possible for them to review your work immediately.

Thanks again!


🙏 Please, if you or your company is finding typescript-eslint valuable, help us sustain the project by sponsoring it transparently on https://opencollective.com/typescript-eslint.

Copy link

netlify bot commented Oct 16, 2024

Deploy Preview for typescript-eslint ready!

Name Link
🔨 Latest commit 90b9c25
🔍 Latest deploy log https://app.netlify.com/sites/typescript-eslint/deploys/67116ce26227610007f7daf8
😎 Deploy Preview https://deploy-preview-10160--typescript-eslint.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 98 (🔴 down 1 from production)
Accessibility: 100 (no change from production)
Best Practices: 92 (no change from production)
SEO: 90 (no change from production)
PWA: 80 (no change from production)
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

nx-cloud bot commented Oct 16, 2024

☁️ Nx Cloud Report

CI is running/has finished running commands for commit 90b9c25. As they complete they will appear below. Click to see the status, the terminal output, and the build insights.

📂 See all runs for this CI Pipeline Execution


✅ Successfully ran 2 targets

Sent with 💌 from NxCloud.

@kirkwaiblinger

This comment was marked as outdated.

Copy link

codecov bot commented Oct 17, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 86.17%. Comparing base (7effdea) to head (90b9c25).
Report is 17 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #10160      +/-   ##
==========================================
+ Coverage   86.15%   86.17%   +0.02%     
==========================================
  Files         429      429              
  Lines       15005    15029      +24     
  Branches     4353     4360       +7     
==========================================
+ Hits        12927    12951      +24     
  Misses       1729     1729              
  Partials      349      349              
Flag Coverage Δ
unittest 86.17% <100.00%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...lugin/src/rules/consistent-indexed-object-style.ts 97.59% <100.00%> (+0.98%) ⬆️
...-plugin/src/rules/restrict-template-expressions.ts 100.00% <ø> (ø)
packages/eslint-plugin/src/rules/typedef.ts 98.61% <ø> (ø)
...ckages/scope-manager/src/referencer/VisitorBase.ts 88.23% <ø> (ø)

@kirkwaiblinger kirkwaiblinger marked this pull request as ready for review October 17, 2024 19:55
Copy link
Member

@JoshuaKGoldberg JoshuaKGoldberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great to me, thanks! 🚀

Excited to have this extra bit of consistency.

TypeScript supports defining arbitrary object keys using an index signature. TypeScript also has a builtin type named `Record` to create an empty object defining only an index signature. For example, the following types are equal:
TypeScript supports defining arbitrary object keys using an index signature or mapped type.
TypeScript also has a builtin type named `Record` to create an empty object defining only an index signature.
For example, the following types are equal:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Praise] 😄 I like the .\n splitting

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did it just for you 😁

@JoshuaKGoldberg JoshuaKGoldberg added the 1 approval >=1 team member has approved this PR; we're now leaving it open for more reviews before we merge label Oct 21, 2024
@JoshuaKGoldberg JoshuaKGoldberg merged commit 9c956ee into typescript-eslint:main Oct 23, 2024
64 checks passed
@kirkwaiblinger kirkwaiblinger deleted the prefer-record-over-in branch October 23, 2024 22:48
renovate bot added a commit to mmkal/eslint-plugin-mmkal that referenced this pull request Oct 28, 2024
##### [v8.12.0](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8120-2024-10-28)

##### 🚀 Features

-   **eslint-plugin:** \[no-base-to-string] handle String() ([#10005](typescript-eslint/typescript-eslint#10005))
-   **eslint-plugin:** \[switch-exhaustiveness-check] add allowDefaultCaseMatchUnionMember option ([#9954](typescript-eslint/typescript-eslint#9954))
-   **eslint-plugin:** \[consistent-indexed-object-style] report mapped types ([#10160](typescript-eslint/typescript-eslint#10160))
-   **eslint-plugin:** \[prefer-nullish-coalescing] add support for assignment expressions ([#10152](typescript-eslint/typescript-eslint#10152))

##### ❤️  Thank You

-   Abraham Guo
-   Kim Sang Du [@developer-bandi](https://github.com/developer-bandi)
-   Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger)
-   YeonJuan [@yeonjuan](https://github.com/yeonjuan)

You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website.
renovate bot added a commit to andrei-picus-tink/auto-renovate that referenced this pull request Oct 30, 2024
| datasource | package                          | from   | to     |
| ---------- | -------------------------------- | ------ | ------ |
| npm        | @typescript-eslint/eslint-plugin | 8.11.0 | 8.12.2 |
| npm        | @typescript-eslint/parser        | 8.11.0 | 8.12.2 |


## [v8.12.2](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8122-2024-10-29)

##### 🩹 Fixes

-   **eslint-plugin:** \[switch-exhaustiveness-check] invert `considerDefaultExhaustiveForUnions` ([#10223](typescript-eslint/typescript-eslint#10223))

##### ❤️  Thank You

-   Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger)

You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website.


## [v8.12.1](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8121-2024-10-28)

This was a version bump only for eslint-plugin to align it with other projects, there were no code changes.

You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website.


## [v8.12.0](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8120-2024-10-28)

##### 🚀 Features

-   **eslint-plugin:** \[no-base-to-string] handle String() ([#10005](typescript-eslint/typescript-eslint#10005))
-   **eslint-plugin:** \[switch-exhaustiveness-check] add allowDefaultCaseMatchUnionMember option ([#9954](typescript-eslint/typescript-eslint#9954))
-   **eslint-plugin:** \[consistent-indexed-object-style] report mapped types ([#10160](typescript-eslint/typescript-eslint#10160))
-   **eslint-plugin:** \[prefer-nullish-coalescing] add support for assignment expressions ([#10152](typescript-eslint/typescript-eslint#10152))

##### ❤️  Thank You

-   Abraham Guo
-   Kim Sang Du [@developer-bandi](https://github.com/developer-bandi)
-   Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger)
-   YeonJuan [@yeonjuan](https://github.com/yeonjuan)

You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website.
renovate bot added a commit to andrei-picus-tink/auto-renovate that referenced this pull request Oct 30, 2024
| datasource | package                          | from   | to     |
| ---------- | -------------------------------- | ------ | ------ |
| npm        | @typescript-eslint/eslint-plugin | 8.11.0 | 8.12.2 |
| npm        | @typescript-eslint/parser        | 8.11.0 | 8.12.2 |


## [v8.12.2](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8122-2024-10-29)

##### 🩹 Fixes

-   **eslint-plugin:** \[switch-exhaustiveness-check] invert `considerDefaultExhaustiveForUnions` ([#10223](typescript-eslint/typescript-eslint#10223))

##### ❤️  Thank You

-   Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger)

You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website.


## [v8.12.1](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8121-2024-10-28)

This was a version bump only for eslint-plugin to align it with other projects, there were no code changes.

You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website.


## [v8.12.0](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8120-2024-10-28)

##### 🚀 Features

-   **eslint-plugin:** \[no-base-to-string] handle String() ([#10005](typescript-eslint/typescript-eslint#10005))
-   **eslint-plugin:** \[switch-exhaustiveness-check] add allowDefaultCaseMatchUnionMember option ([#9954](typescript-eslint/typescript-eslint#9954))
-   **eslint-plugin:** \[consistent-indexed-object-style] report mapped types ([#10160](typescript-eslint/typescript-eslint#10160))
-   **eslint-plugin:** \[prefer-nullish-coalescing] add support for assignment expressions ([#10152](typescript-eslint/typescript-eslint#10152))

##### ❤️  Thank You

-   Abraham Guo
-   Kim Sang Du [@developer-bandi](https://github.com/developer-bandi)
-   Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger)
-   YeonJuan [@yeonjuan](https://github.com/yeonjuan)

You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website.
renovate bot added a commit to andrei-picus-tink/auto-renovate that referenced this pull request Oct 31, 2024
| datasource | package                          | from   | to     |
| ---------- | -------------------------------- | ------ | ------ |
| npm        | @typescript-eslint/eslint-plugin | 8.11.0 | 8.12.2 |
| npm        | @typescript-eslint/parser        | 8.11.0 | 8.12.2 |


## [v8.12.2](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8122-2024-10-29)

##### 🩹 Fixes

-   **eslint-plugin:** \[switch-exhaustiveness-check] invert `considerDefaultExhaustiveForUnions` ([#10223](typescript-eslint/typescript-eslint#10223))

##### ❤️  Thank You

-   Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger)

You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website.


## [v8.12.1](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8121-2024-10-28)

This was a version bump only for eslint-plugin to align it with other projects, there were no code changes.

You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website.


## [v8.12.0](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8120-2024-10-28)

##### 🚀 Features

-   **eslint-plugin:** \[no-base-to-string] handle String() ([#10005](typescript-eslint/typescript-eslint#10005))
-   **eslint-plugin:** \[switch-exhaustiveness-check] add allowDefaultCaseMatchUnionMember option ([#9954](typescript-eslint/typescript-eslint#9954))
-   **eslint-plugin:** \[consistent-indexed-object-style] report mapped types ([#10160](typescript-eslint/typescript-eslint#10160))
-   **eslint-plugin:** \[prefer-nullish-coalescing] add support for assignment expressions ([#10152](typescript-eslint/typescript-eslint#10152))

##### ❤️  Thank You

-   Abraham Guo
-   Kim Sang Du [@developer-bandi](https://github.com/developer-bandi)
-   Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger)
-   YeonJuan [@yeonjuan](https://github.com/yeonjuan)

You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website.
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 1, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
1 approval >=1 team member has approved this PR; we're now leaving it open for more reviews before we merge
Projects
None yet
3 participants