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

feat(eslint-plugin): replace ban-types with no-restricted-types, no-unsafe-function-type, no-wrapper-object-types#9102

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

Conversation

JoshuaKGoldberg
Copy link
Member

@JoshuaKGoldbergJoshuaKGoldberg commentedMay 15, 2024
edited
Loading

BREAKING CHANGE:
Replaces rules in the recommended configs.

PR Checklist

Overview

Finally deprecates the venerableban-types rule and adds three new rules:

  • 🆕no-restricted-types: allowing banning a configurable list of type names
  • 🆕no-unsafe-function-type: banning the built-inFunction
  • 🆕no-wrapper-object-types: banningObject and built-in class wrappers such asNumber

💖

@JoshuaKGoldbergJoshuaKGoldberg added the breaking changeThis change will require a new major version to be released labelMay 15, 2024
@JoshuaKGoldbergJoshuaKGoldberg added this to the8.0.0 milestoneMay 15, 2024
@typescript-eslint
Copy link
Contributor

Thanks for the PR,@JoshuaKGoldberg!

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.

@netlifyNetlify
Copy link

netlifybot commentedMay 15, 2024
edited
Loading

Deploy Preview fortypescript-eslint ready!

NameLink
🔨 Latest commit75543e9
🔍 Latest deploy loghttps://app.netlify.com/sites/typescript-eslint/deploys/666ab8d63628680008c109fd
😎 Deploy Previewhttps://deploy-preview-9102--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: 99 (no change 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 yourNetlify site configuration.

@nx-cloudNx Cloud
Copy link

nx-cloudbot commentedMay 15, 2024
edited
Loading

☁️ Nx Cloud Report

CI is running/has finished running commands for commit75543e9. 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 1 target

Sent with 💌 fromNxCloud.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I don't like this name 😦 but couldn't think of one I really do like...no-primitive-class-wrappers?no-class-wrapper-types? So wordy, but I also like ending with-types... Someone please help 🙏

Choose a reason for hiding this comment

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

TL;DRban-bad-built-in-class-types or even justban-bad-class-types


wordy? you must hateuse-unknown-in-catch-callback-variable 😆

I guess the commonalities in this rule are, for type X flagged by the rule

  • X is built-in ("built-in", "intrinsic")
  • the types means "something which has X.prototype in its prototype chain". (gives us "class-type"/"object-type"/"prototype-type"/etc) <-- This makes the rule hard to name
  • that type is bad for some reason, but not necessarily the same reason...
    • in the case ofBigInt,Boolean,Number,String,Symbol, because they refer to the boxed primitive, but the unboxed primitive is also assignable
    • Object, because it's terrible; any primitive is assignable, similar to previous
    • Function, because it's unsound <-- This makes the rule hard to name
    • Technically we could argue all of these are unsound, butFunction is really a difference in kind compared to the others, so it's hard to justify using "unsound"/"unsafe" in the name
  • X is uppercase -technically true but not really the point 😄

So that would lead me to a rough template of{no/ban}-bad-{built-in/intrinsic}-{instance/prototype/class/object}-types of which my tentative favorite isban-bad-built-in-class-types.


I'm not a fan of "wrapper" sinceObject andFunction aren't wrappers. And same problem arises with using the word "primitive". IfFunction weren't part of the rule though I thinkno-{boxed/wrapped}-primitive-type would work nicely 🤷

Copy link
Contributor

Choose a reason for hiding this comment

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

👋 What aboutno-ts-wrapper-types ?

Pro: These types are all defined by TypeScript itself, and the list of banned types not only includes primitive wrappers, but also wrappers forobject and a wide, any-ishfunction which are technically not primitivesper MDN. The name indicates it's related to a TypeScript feature itself likeban-ts-comment does.

Con: Might be a little more confusing / need further explanation in the docs.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

wordy? you must hateuse-unknown-in-catch-callback-variable 😆

Haha, yes, I do! And now I'm realizing maybe we could have called it justuse-unknown-in-catch-callback. Ugh.

Long rule names make for less readable/pronouncable editor popups & CLI complaints. They're actively (mildly) worse for users.

kirkwaiblinger reacted with thumbs up emoji
Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Some more suggestions from across the internet:

Some general thoughts...

  • "Primitive"would be something nice to include in there, but as@Josh-Cena pointed out,object isn't a primitive and so that's inaccurate
  • MovingFunction bans into a separate rule (no-unsafe-call: ban calling Function #9108 (comment)) would help narrow down the naming
    • It'd be nice for naming to also move outObject bans into its own rule so we could then use"primitive" after all, but I don't know of any justifications on that one 😕
  • It's hard to justify using a term that isn't in common usage already. Words like"boxed" and"primordial" might (previously or presently) be good descriptors, but if they don't match MDN docs or common StackOverflow nomenclature, users will have a hard time with them
  • We don't generally put abbreviations in rule names anymore. It's hard enough for users to understand lint rules, let alone have to remember random abbreviations in there too.
  • I think it'd be nice to include some indication in the name that it's abouttypes /type annotations, not generally using those things. E.g. ending with-types. That'd help disambiguate from other rules that do banusage of some types (e.g.no-unsafe-* foranys).

That leaves a few finalists IMO:

  • no-class-wrapper-types
  • no-intrinsic-wrapper-types
  • no-intrinsic-class-wrapper-types
  • no-primitive-class-wrapper-types
  • prefer-lowercase-intrinsic-types:if we split outFunction into its own rule, since lower-casefunction isn't a type

I'll continue to ruminate...

davidlj95 and kirkwaiblinger reacted with thumbs up emoji
Copy link
Collaborator

Choose a reason for hiding this comment

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

JoshuaKGoldberg reacted with thumbs up emoji
Copy link

@bennycodebennycodeMay 24, 2024
edited
Loading

Choose a reason for hiding this comment

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

I can see how the term "class" could be confusing as it could be associated with limiting the behaviour in a realclass. While reading through the MDN docs, I came across this:

When properties are accessed on primitives, JavaScriptauto-boxes the value into awrapper object and accesses the property on that object instead. For example, "foo".includes("f") implicitly creates aStringwrapper object and callsString.prototype.includes() on that object. Thisauto-boxing behavior is not observable in JavaScript code but is a good mental model of various behaviors

Source:https://developer.mozilla.org/en-US/docs/Glossary/Primitive

Stating the above, would these names be acceptable?

  • no-wrapper-object-types
  • no-auto-boxing-types

Copy link
Member

@kirkwaiblingerkirkwaiblingerMay 25, 2024
edited
Loading

Choose a reason for hiding this comment

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

@kirkwaiblinger what do you mean aboutObject not being a wrapper?

Oh, well, I think I meant the Object type doesn't conceptually refer to a wrapper around a primitive, though as you pointed out (and I wasn't aware) thatis a subset of its possible uses. So, feel free to disregard my comment on that; my mind has been changed 🙂

JoshuaKGoldberg reacted with thumbs up emojiJoshuaKGoldberg reacted with laugh emoji
Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I likeno-wrapper-object-types a lot!"Wrapper" might be a bit general on its own, but the built-in upper-case types are the only ones I can think of that that jump to mind as"wrapper object types". Nice@bennycode!

Renaming now, but if anybody doesn't like this then please do continue the conversation. 🙁

kirkwaiblinger and bachmacintosh reacted with thumbs up emoji
Copy link
MemberAuthor

@JoshuaKGoldbergJoshuaKGoldbergJun 2, 2024
edited
Loading

Choose a reason for hiding this comment

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

Update: following#9102 (comment), I split out ano-unsafe-function-type.@Josh-Cena makes a good point that the function type isn't a wrapper.

That leaves the question of whether theno-wrapper-object-types name is ok to includeObject. I.e. being ok with what@kirkwaiblinger mentioned: that it's not technically a wrapper itself. I'm in favor of allowing this: more convenience at the cost ofslightly less pedantically correct rule name. But I could be convinced otherwise if we have a good rule name alternative and/or have justification to moveObject into its own rule (do we?).

@JoshuaKGoldbergJoshuaKGoldberg requested a review froma teamMay 15, 2024 23:57

Choose a reason for hiding this comment

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

TL;DRban-bad-built-in-class-types or even justban-bad-class-types


wordy? you must hateuse-unknown-in-catch-callback-variable 😆

I guess the commonalities in this rule are, for type X flagged by the rule

  • X is built-in ("built-in", "intrinsic")
  • the types means "something which has X.prototype in its prototype chain". (gives us "class-type"/"object-type"/"prototype-type"/etc) <-- This makes the rule hard to name
  • that type is bad for some reason, but not necessarily the same reason...
    • in the case ofBigInt,Boolean,Number,String,Symbol, because they refer to the boxed primitive, but the unboxed primitive is also assignable
    • Object, because it's terrible; any primitive is assignable, similar to previous
    • Function, because it's unsound <-- This makes the rule hard to name
    • Technically we could argue all of these are unsound, butFunction is really a difference in kind compared to the others, so it's hard to justify using "unsound"/"unsafe" in the name
  • X is uppercase -technically true but not really the point 😄

So that would lead me to a rough template of{no/ban}-bad-{built-in/intrinsic}-{instance/prototype/class/object}-types of which my tentative favorite isban-bad-built-in-class-types.


I'm not a fan of "wrapper" sinceObject andFunction aren't wrappers. And same problem arises with using the word "primitive". IfFunction weren't part of the rule though I thinkno-{boxed/wrapped}-primitive-type would work nicely 🤷

Co-authored-by: Kirk Waiblinger <kirk.waiblinger@gmail.com>
@JoshuaKGoldbergJoshuaKGoldberg changed the titlefeat(eslint-plugin): add no-restricted-types, no-uppercase-alias-types rulesfeat(eslint-plugin): add no-restricted-types, no-wrapper-object-types rulesMay 26, 2024
Copy link
Member

@kirkwaiblingerkirkwaiblinger left a comment

Choose a reason for hiding this comment

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

It's all coming together

davidlj95 and JoshuaKGoldberg reacted with laugh emojiJoshuaKGoldberg and davidlj95 reacted with rocket emoji
@kirkwaiblingerkirkwaiblinger added the 1 approval>=1 team member has approved this PR; we're now leaving it open for more reviews before we merge labelMay 27, 2024
@@ -15,6 +15,16 @@ It's often a good idea to ban certain types to help with consistency and safety.
This rule bans specific types and can suggest alternatives.
Note that it does not ban the corresponding runtime objects from being used.

:::danger Deprecated
This rule is deprecated and now split across several rules:
Copy link
Member

Choose a reason for hiding this comment

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

Why are we deprecating the rule?
There's still value in the rule - just that the default config can be empty now.

People use this rule all the time to ban various types across their codebase. It's the only way to reliably target type names only without hitting value names.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

@bradzacher because it's essentially being renamed tono-restricted-types, with the default values split into more targeted rules. I'll update the deprecation notice to make it more clear.

Copy link
Member

Choose a reason for hiding this comment

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

Given this is a major - should we just remove the old rule name entirely?
Is there a benefit to keeping it if we're adding the new rules to recommended and the old rule should almost be a 1:1 name swap in config?

Copy link
MemberAuthor

@JoshuaKGoldbergJoshuaKGoldbergMay 27, 2024
edited
Loading

Choose a reason for hiding this comment

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

I was thinking this is a pretty user-known rule so leaving it in would help with migration. The name swap might be 1:1 but the behavioral change means figuring out which of the three new rules individual config entries / inline comments map to.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

...heck, now that I post this, I'm liking deletingban-types and just leaving a tombstone page. cc @typescript-eslint/triage-team - we can always add back the old deleted rule if trying out on community repos surfaces a lot of pain (#9141), right?

kirkwaiblinger and bradzacher reacted with thumbs up emoji

Choose a reason for hiding this comment

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

Makes sense to me too...
If we do that for this rule, should we just go ahead and do that for#8821, too, though?

Copy link
Member

Choose a reason for hiding this comment

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

@kirkwaiblinger we can land#8821 against main and then remove it in v8
similarly we could technically land this PR in main and then removeban-types in v8 (which is what I originally suggested#8977 (comment)).

I.e. we could backport the new rules to v7 so they're available right now, deprecateban-types, and then do the actual breaking change (removingban-types) in v8.

kirkwaiblinger reacted with thumbs up emoji
Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I like it. Once this PR goes in I can send a followup to backport the changes to v7 /main, replacing the deletion with a deprecation.

kirkwaiblinger reacted with thumbs up emoji
@JoshuaKGoldbergJoshuaKGoldberg changed the titlefeat(eslint-plugin): add no-restricted-types, no-wrapper-object-types rulesfeat(eslint-plugin): replace ban-types with no-restricted-types, no-wrapper-object-types rulesMay 27, 2024
}

return {
TSClassImplements(node): void {
Copy link
Member

Choose a reason for hiding this comment

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

Thought: should the two new rules checkimplements andextends? You can't extendnumber or() => {} so the use ofNumber must either be intentional or refer to another type (since we still don't have scope analysis for this rule)

Copy link
MemberAuthor

@JoshuaKGoldbergJoshuaKGoldbergJun 10, 2024
edited
Loading

Choose a reason for hiding this comment

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

Someone could, in theory, be doing:

  • extends:interface MyFunction extends Function { __brand: 'my-function'; }
  • implements:class Weird implements Function { apply: ...; ... }.

...so for completeness, I think we need to?

Copy link
Member

Choose a reason for hiding this comment

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

Is that a bad idea? If they do it there's no reason they are doing it by accident or ignorance.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I'm not strongly opposed to removing this for simplicity. It does feel a little weird to me to leave in a known thing that nobody should really ever do - even if it's only doable with great intent.

Copy link
Member

Choose a reason for hiding this comment

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

Let's leave them in for now, but at leastextends Function feels fine to me.

JoshuaKGoldberg reacted with thumbs up emoji
@JoshuaKGoldbergJoshuaKGoldberg removed the 1 approval>=1 team member has approved this PR; we're now leaving it open for more reviews before we merge labelJun 3, 2024
@Josh-Cena
Copy link
Member

Leaving my verbal approval, pending some discussion but not a blocker since we have that behavior already

JoshuaKGoldberg reacted with thumbs up emoji

Copy link
Member

@kirkwaiblingerkirkwaiblinger left a comment

Choose a reason for hiding this comment

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

Some nits and stuff but looks good to me 👍

Similarly, TypeScript's `Function` type refers to objects created with the `Function` constructor, i.e. `new Function("eval'd code with implicit args")`.
`Function` allows being called with any number of arguments and returns type `any`.
It's generally better to specify function parameter and return types with function type syntax.
Using the primitives like `0` instead of object wrappers like `new Number(0)` is generally considered a JavaScript best practice.

Choose a reason for hiding this comment

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

For flow, you might link this and the next sentence with a conjunction such as "..., since ..." or something like that.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I like the idea but can't think of phrasing I like here to go with it. Do you have a suggestion?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

@kirkwaiblinger I really want to get this in to unblock testing on community repos, so I'm merging now - but let's definitely revisit the docs before v8 goes stable!

Choose a reason for hiding this comment

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

JoshuaKGoldbergand others added2 commitsJune 12, 2024 03:52
Co-authored-by: Kirk Waiblinger <kirk.waiblinger@gmail.com>
Copy link
Member

@kirkwaiblingerkirkwaiblinger left a comment

Choose a reason for hiding this comment

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

🔥🔥

Copy link
Member

@bradzacherbradzacher left a comment

Choose a reason for hiding this comment

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

My one feedback:
These two rules must use scope analysis to ensure the reference is to the global type, nor a local one.

Eg this code should be fine:

typeT<Symbol>=Symbol;typeU<UU>=UUextendsT<inferFunction>  ?Function :never;

Without scope analysis I believe the first case above will autofix totype T<Symbol> = symbol; and break the code!

I say this because we specifically received this feedback in the past from users but had rejected it because "ban-types is intentionally dumb and simple and just bans the name".
Given these rules are now targeted and toight - there's no reason we can't now add the scope analysis to be safe.


Otherwise LGTM!
sobeautiful.gif


Is this all documented in the v8 blog post?

JoshuaKGoldberg reacted with thumbs up emoji
@@ -0,0 +1,22 @@
:::danger Deprecated
Copy link
Member

Choose a reason for hiding this comment

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

press-f-pay-respect.gif

JoshuaKGoldberg reacted with heart emoji
Copy link
Member

@bradzacherbradzacher left a comment

Choose a reason for hiding this comment

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

@JoshuaKGoldberg
Copy link
MemberAuthor

JoshuaKGoldberg commentedJun 13, 2024
edited
Loading

Merging this now withroughly resolved conversations (except some more docs work) to unblock#9141.

Note that this is intov8, notmain - we can definitely clean things up & make any changes we need inv8 as needed before this goes stable!

@JoshuaKGoldbergJoshuaKGoldberg merged commit5cd80ca intotypescript-eslint:v8Jun 13, 2024
59 checks passed
@JoshuaKGoldbergJoshuaKGoldberg deleted the no-ban-types-splitup branchJune 13, 2024 09:34
@github-actionsgithub-actionsbot locked asresolvedand limited conversation to collaboratorsJun 25, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account?Sign in.
Reviewers

@bennycodebennycodebennycode left review comments

@bachmacintoshbachmacintoshbachmacintosh left review comments

@jakebaileyjakebaileyjakebailey left review comments

@kirkwaiblingerkirkwaiblingerkirkwaiblinger approved these changes

@bradzacherbradzacherbradzacher approved these changes

@Josh-CenaJosh-CenaAwaiting requested review from Josh-Cena

Assignees
No one assigned
Labels
breaking changeThis change will require a new major version to be released
Projects
None yet
Milestone
8.0.0
Development

Successfully merging this pull request may close these issues.

7 participants
@JoshuaKGoldberg@Josh-Cena@bennycode@bachmacintosh@jakebailey@bradzacher@kirkwaiblinger

[8]ページ先頭

©2009-2025 Movatter.jp