Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

fix(eslint-plugin): [ban-types] Suggest usingobject to mean "any object"#5018

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Closed

Conversation

@RyanCavanaugh
Copy link

People vexed by

declareletj:unknown;letm={ ...(jas{})};

being banned byban-types's default of disallowing{} have resorted to using

letm={ ...(jasnever)};

which TS 4.7 flags as an error, for good reason. The alternatives suggested by this rule are all inappropriate for the situation:

// Doesn't fix anythingletm2={ ...(jasunknown)};// Adds undesirable index signature to result typeletm3={ ...(jasRecord<string,never>)};// Adds undesirable index signature to result typeletm4={ ...(jasRecord<string,unknown>)};

The "correct" fix is

letm={ ...(jasobject)};

Now thatobject has been shipping in TS for several years, this seems like a safe suggestion in lieu ofRecord's messy side effects

PR Checklist

Overview

Methuselah96 reacted with thumbs up emoji
People vexed by```tsdeclare let j: unknown;let m = { ...(j as {}) };```being banned by `ban-types`'s default of disallowing `{}` have resorted to using```tslet m = { ...(j as never) };```which TS 4.7 flags as an error, for good reason. The alternatives suggested by this rule are all inappropriate for the situation:```ts// Doesn't fix anythinglet m2 = { ...(j as unknown) };// Adds undesirable index signature to result typelet m3 = { ...(j as Record<string, never>) };// Adds undesirable index signature to result typelet m4 = { ...(j as Record<string, unknown>) };```The "correct" fix is```tslet m = { ...(j as object) };```Now that `object` has been shipping in TS for several years, this seems like a safe suggestion in lieu of `Record`'s messy side effects
@nx-cloud
Copy link

nx-cloudbot commentedMay 19, 2022
edited
Loading

☁️ Nx Cloud Report

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

📂 See all runs for this branch


✅ Successfully ran 47 targets

Sent with 💌 fromNxCloud.

@netlify
Copy link

netlifybot commentedMay 19, 2022

👷 Deploy request fortypescript-eslint pending review.

Visit the deploys page to approve it

NameLink
🔨 Latest commita4c9e0c

@typescript-eslint
Copy link
Contributor

Thanks for the PR,@RyanCavanaugh!

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 onhttps://opencollective.com/typescript-eslint. As a thank you, your profile/company logo will be added to our main README which receives thousands of unique visitorsper day.

@codecov
Copy link

codecovbot commentedMay 19, 2022
edited
Loading

Codecov Report

Merging#5018 (a4c9e0c) intomain (7e7b24c) willincrease coverage by2.46%.
The diff coverage isn/a.

@@            Coverage Diff             @@##             main    #5018      +/-   ##==========================================+ Coverage   91.32%   93.79%   +2.46%==========================================  Files         132      286     +154       Lines        1487     9795    +8308       Branches      224     2930    +2706     ==========================================+ Hits         1358     9187    +7829- Misses         65      328     +263- Partials       64      280     +216
FlagCoverage Δ
unittest93.79% <ø> (+2.46%)⬆️

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

Impacted FilesCoverage Δ
packages/eslint-plugin/src/rules/ban-types.ts100.00% <ø> (ø)
packages/eslint-plugin/src/rules/no-namespace.ts100.00% <0.00%> (ø)
...n/src/rules/indent-new-do-not-use/OffsetStorage.ts100.00% <0.00%> (ø)
...lugin/src/rules/no-confusing-non-null-assertion.ts82.60% <0.00%> (ø)
...ackages/eslint-plugin/src/rules/no-var-requires.ts90.00% <0.00%> (ø)
...es/eslint-plugin/src/rules/no-loss-of-precision.ts83.33% <0.00%> (ø)
...nt-plugin/src/rules/indent-new-do-not-use/index.ts98.09% <0.00%> (ø)
...eslint-plugin/src/rules/prefer-return-this-type.ts91.80% <0.00%> (ø)
...plugin/src/rules/naming-convention-utils/shared.ts100.00% <0.00%> (ø)
packages/eslint-plugin/src/rules/no-unused-vars.ts95.90% <0.00%> (ø)
... and145 more

@bradzacher
Copy link
Member

bradzacher commentedMay 19, 2022
edited
Loading

The issue withobject as a suggested replacement has always been that it's really hard use (microsoft/TypeScript#21732).

Once you've got anobject, you can't really do anything with it:

functionfoo(arg:object){arg.foo;// can't access random propertiesif('foo'inarg){arg.foo;// can't do guarded random property access either}}

repl

So as a "type meaning "any object"" replacement -object is not great!

OTOHRecord<string, unknown> allows you to operate on the variable as an object and do everything one might expect of an object - so for the general case it's much more usable:

functionfoo(arg:Record<string,unknown>){arg.foo;// can access random propertiesif('foo'inarg){arg.foo;// can do guarded random property access either}}

repl

Really once you've got anobject, you need some form of a user-defined type guard to get away fromobject - and most cases that type guard will convert it to aRecord<string, unknown> anyways.

@RyanCavanaugh
Copy link
Author

RyanCavanaugh commentedMay 19, 2022
edited
Loading

I don't feel like you addressed the scenario (spread) covered in the OP. All of the workarounds make the resultant typeless safe by adding an index signature tom that allows arbitrary reads/writes, and you said the entire point of the default of{} wasabout not creating big type holes

@bradzacher
Copy link
Member

Is it common that people want to spread anunknown?
I personally haven't seen too many codebases attempt such an operation, but I admittedly am not exposed to a large spread of other codebases now-a-days.


Definitely correct that it's technically a safety hole in of itself.
Ideally the suggestion would beReadonly<Record<string, unknown>> - but people get angry when you suggest long types like that without a fixer - generally leading people to just use VSCode's quick fix disable instead.
Also from experience across TS codebases people tend to not usereadonly too much and instead rely on implicit assumptions that thou shalt not mutate this.


I'd be more than happy if you want to specifically mention the spread case in the error message to help inform people of the right way to do things!
But I don't know if usingobject as the defacto "any object" replacement is necessarily the best thing to do.

@RyanCavanaugh
Copy link
Author

RyanCavanaugh commentedMay 20, 2022
edited
Loading

The reason I bring this up is we found some new breaks (e.g.https://github.com/facebook/docusaurus/blob/c16a08cba59bea5e48d3a79cee82c23aaae29876/packages/docusaurus-theme-classic/src/theme/CodeBlock/Container/index.tsx#L26 ) due to TS 4.7 treating a spread ofnever as an error (since, in reality, we have no idea what's about to happen). Thebest assertion here is probablyobject (which used to be banned byban-types, which I'm guessing is why the author didn't use that), with the second-best being{ }, which is also banned. So instead they asserted tonever, which is a) super wrong in code that actually executes and b) now broken.

If people want an object, meaning something whose proto is at leastObject.prototype, thenobject really is the best fit for that. The variousRecord things here are distant seconds and thirds IMO. It feels like the rule didn't recommendobject due to its old default configuration also banning the use ofobject, which I was glad to see removed.

Sort of an aside and I'll likely be opening a new issue: There's just a lot of confusion likely to come down the road on this specific ban in general, since we're likely to change the definition ofNonNullable<T> toT & { } in 4.8, and are already in 4.8 going to change the default narrowing rules such that a truthy narrowing of a value of an unconstrained type parameter becomesT & { }. Having typescript-eslint, out of the box, ban you from writing a type that TypeScript itself is inferring under totally normal operation, is awkward.

The rest ofban-types is totally solid and I'd really raise reconsidering{ } being in the default ruleset forban-types, since the other bans it defaults to (String,Object, etc) are extremely well-grounded.

@Josh-Cena
Copy link
Member

FWIW,props as object doesn't fix the spreading props problem either.

I'm ambivalent on banning{}. It's useful for people who understand what it does, but it doesn't follow new users' intuitions (although there's nothing unsound about it; you can write{ length: number } and assign a string to it as well).

@github-actionsgithub-actionsbot locked asresolvedand limited conversation to collaboratorsJul 21, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account?Sign in.

Reviewers

No reviews

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

3 participants

@RyanCavanaugh@bradzacher@Josh-Cena

[8]ページ先頭

©2009-2025 Movatter.jp