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): [prefer-readonly-parameter-types] handle class sharp private field and member without throwing error#4343

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

lonyele
Copy link
Contributor

@lonyelelonyele commentedDec 24, 2021
edited
Loading

PR Checklist

Overview

It was my first time diving into typescript code and it was a rabbit hole... I feel like it's a bug from typescript that other symbols have the sameescapedName andname likeescapedName: "__@foo@10", name: "__@foo@10" but this# private identifiers have differentescapedName andname likeescapedName: "__#1@#foo", name: "#foo". This is why the code goes intochecker.getTypeOfPropertyOfType(type, name) and returnundefined that at the end throws the error.

I stepped intoconst type = checker.getTypeAtLocation(tsNode) to find whyescapedName andname are different but failed...even going into the end

Aside from handling this error case fromtypescript-eslint side. I'm curious how this rule should handles the Class type. The code is handling parameter properties but what about some complicated combos of public/private fields and members? I haven't really used to this extend so I'm purely interested in what behavior is expected. I found that making read-only member in class is impossible but private or this# private is possible which is automatically readonly? more precisely just cannot be accessed on compile(private) or on runtime(# private)

@nx-cloud
Copy link

nx-cloudbot commentedDec 24, 2021
edited
Loading

☁️ Nx Cloud Report

CI ran the following commands for commit3d8acb4. Click to see the status, the terminal output, and the build insights.

📂 See all runs for this branch


✅ Successfully ran 48 targets

Sent with 💌 fromNxCloud.

@typescript-eslint
Copy link
Contributor

Thanks for the PR,@lonyele!

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.

@netlify
Copy link

netlifybot commentedDec 24, 2021
edited
Loading

❌ Deploy Preview fortypescript-eslint failed.

🔨 Explore the source changes:3d8acb4

🔍 Inspect the deploy log:https://app.netlify.com/sites/typescript-eslint/deploys/621cfd42804d350008c96c22

@codecov
Copy link

codecovbot commentedDec 24, 2021
edited
Loading

Codecov Report

Merging#4343 (3d8acb4) intomain (d698f6b) willincrease coverage by0.34%.
The diff coverage is100.00%.

@@            Coverage Diff             @@##             main    #4343      +/-   ##==========================================+ Coverage   92.49%   92.83%   +0.34%==========================================  Files         346      164     -182       Lines       11696     8490    -3206       Branches     3340     2725     -615     ==========================================- Hits        10818     7882    -2936+ Misses        608      406     -202+ Partials      270      202      -68
FlagCoverage Δ
unittest92.83% <100.00%> (+0.34%)⬆️

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

Impacted FilesCoverage Δ
packages/type-utils/src/isTypeReadonly.ts85.14% <100.00%> (+0.45%)⬆️
packages/type-utils/src/propertyTypes.ts90.90% <100.00%> (+3.40%)⬆️
...ges/eslint-plugin/src/rules/no-misused-promises.ts98.63% <0.00%> (-1.37%)⬇️
...ckages/eslint-plugin/src/util/getESLintCoreRule.ts75.00% <0.00%> (ø)
...plugin/src/rules/explicit-module-boundary-types.ts91.04% <0.00%> (ø)
...ackages/scope-manager/src/variable/VariableBase.ts
packages/typescript-estree/src/version-check.ts
packages/scope-manager/src/lib/esnext.intl.ts
...kages/scope-manager/src/referencer/ClassVisitor.ts
packages/scope-manager/src/scope/FunctionScope.ts
... and190 more

@bradzacherbradzacher added the bugSomething isn't working labelDec 27, 2021
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.

I might be missing something - but I'm not sure how this changes the logic.
Before it checked for__<string>, now it checks for__@<string> or__#<string>. Aren't the new checks covered by the old check?

The code is handling parameter properties but what about some complicated combos of public/private fields and members?

The code should just iterate over the properties of the object. If they can be seen as readonly, then it's readonly - else it's mutable. It depends entirely on what the TS APIs reports when the type is interrogated.

I'd assume that the TS APIs would not report the private/protected members unless they're accessible at that location.

Comment on lines 225 to 239
// PrivateIdentifier is handled without throwing error.
{
code: `
class Foo {
readonly #privateField = 'foo';
#privateMember() {}
}
function foo(arg: Foo) {}
`,
options: [
{
treatMethodsAsReadonly: true,
},
],
},
Copy link
Member

Choose a reason for hiding this comment

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

to double check - this case is ignored when#privateField is notreadonly as well?
Could you add a test to verify this please? EG this should not error:

{code:`        class Foo {          #privateField = 'foo';          #privateMember() {}        }        function foo(arg: Foo) {}      `,options:[{treatMethodsAsReadonly:false,},],},

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

For the quick answer, it is not. It fails on test. But not throwing an exception.

    {      code: `        class Foo {          #privateField = 'foo';  ---> test fails becuase it is not readonly          #privateMember() {}.  ---> test fails because "treatMethodsAsReadonly" is set to "false"        }        function foo(arg: Foo) {}      `,      options: [        {          treatMethodsAsReadonly: false,        },      ],    },

Additional info is that before the fix,#privateMember throws an exception whentreatMethodsAsReadonly: true, but no exception and test fails ontreatMethodsAsReadonly: false

After the fix, whether private method withtreatMethodsAsReadonly is set true/false or private field withreadonly or not. There is no exception thrown.

Here are some results of after the fix

{  code: `    class Foo {      #privateField1 = 'foo';     ----> test fails becuase it is not readonly      readonly #privateField2 = 'foo';     -----> test passed because it's readonly      private privateField3 = 'foo';     ----> test fails becuase it's not readonly      private  readonly privateField4 = 'foo';     ----> test passed because it's readonly            #privateMember1() {}     ----> test fails because "treatMethodsAsReadonly" is set false      private #privateMember2() {}   ----> same as above      private privateMember3() {}  ----> same as above    }    function foo(arg: Foo) {}  `,  options: [    {      treatMethodsAsReadonly: false,     },    ],},

Copy link
Member

Choose a reason for hiding this comment

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

Interesting.
Forprivate fields - we 100% should be treating them as "public" (and thus subject them toreadonly checks), however for#private fields, they should be exempt fromreadonly checks.

The intent behind the rule is that you should not be able to mutate the type at runtime. You can accessprivate fields via computed member access syntax, but because you cannot access#private fields at all outside the class - they shouldn't be included in the checks.
(playground)

lonyele reacted with thumbs up emoji
Copy link
ContributorAuthor

@lonyelelonyeleFeb 12, 2022
edited
Loading

Choose a reason for hiding this comment

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

Thanks for the playground. I didn't know about that. I just enjoy all your comments on any issues or prs. I think very constructive(very educational to me) :D

please check thiscommit

@bradzacherbradzacher added the awaiting responseIssues waiting for a reply from the OP or another party labelDec 27, 2021
@lonyele
Copy link
ContributorAuthor

lonyele commentedDec 27, 2021
edited
Loading

I might be missing something - but I'm not sure how this changes the logic. Before it checked for__<string>, now it checks for__@<string> or__#<string>. Aren't the new checks covered by the old check?

The difference is what to check. Changed code useescapedName instead ofname. Technically fix can be simple as this.

from previous code if (!escapedName || !name.startsWith('__')) { ... }to fixed codeif (!escapedName || !escapedName.startsWith('__')) { ... }

I just wrote a little bit more explicitly why.startsWith('__') is needed usingisKnowSymbol andisPrivateIdentifierSymbol with comments.

WhyescapedName should be used instaed ofname? because of this case.#private'sescapedName andname are different.reference
case for escapedName: "__@foo@10", name: "__@foo@10"
case for escapedName: "__#1@#foo", name: "#foo"

bradzacher reacted with thumbs up emoji

@bradzacherbradzacher added awaiting responseIssues waiting for a reply from the OP or another party and removed awaiting responseIssues waiting for a reply from the OP or another party labelsDec 28, 2021
@bradzacher
Copy link
Member

sounds good!
just a response for my above query about the tests and we're good to go

@lonyele
Copy link
ContributorAuthor

lonyele commentedDec 28, 2021
edited
Loading

Not sure why my reply to your request on additional test is not showing on this main page.
But i actually replied it, please go to theFiles Changed section

@bradzacher
Copy link
Member

wierdly I can't see your reply there either.
Could you please try replying again?
You might need to finish "reviewing" the PR to make the comment public.

@@ -7,7 +7,7 @@ export function getTypeOfPropertyOfName(
escapedName?: ts.__String,
): ts.Type | undefined {
// Most names are directly usable in the checker and aren't different from escaped names
if (!escapedName || !name.startsWith('__')) {
if (!escapedName || !isSymbol(escapedName)) {
Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

@JoshuaKGoldberg I also checked your previous pr and this tsutil's handling. I was wondering handling this if statement with usingescapedName is still ok
tsutil handling

Comment on lines 37 to 51

// Symbolic names need to be specially handled because TS api is not sufficient for these cases.
function isSymbol(escapedName: string): boolean {
return isKnownSymbol(escapedName) || isPrivateIdentifierSymbol(escapedName);
}

// case for escapedName: "__@foo@10", name: "__@foo@10"
function isKnownSymbol(escapedName: string): boolean {
return escapedName.startsWith('__@');
}

// case for escapedName: "__#1@#foo", name: "#foo"
function isPrivateIdentifierSymbol(escapedName: string): boolean {
return escapedName.startsWith('__#');
}
Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

I took it from typescript codebase

export function getPropertyNameForUniqueESSymbol(symbol: Symbol): __String {    return `__@${getSymbolId(symbol)}@${symbol.escapedName}` as __String;}export function getSymbolNameForPrivateIdentifier(containingClassSymbol: Symbol, description: __String): __String {    return `__#${getSymbolId(containingClassSymbol)}@${description}` as __String;}export function isKnownSymbol(symbol: Symbol): boolean {    return startsWith(symbol.escapedName as string, "__@");}export function isPrivateIdentifierSymbol(symbol: Symbol): boolean {    return startsWith(symbol.escapedName as string, "__#");}

Comment on lines 225 to 239
// PrivateIdentifier is handled without throwing error.
{
code: `
class Foo {
readonly #privateField = 'foo';
#privateMember() {}
}
function foo(arg: Foo) {}
`,
options: [
{
treatMethodsAsReadonly: true,
},
],
},
Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

For the quick answer, it is not. It fails on test. But not throwing an exception.

    {      code: `        class Foo {          #privateField = 'foo';  ---> test fails becuase it is not readonly          #privateMember() {}.  ---> test fails because "treatMethodsAsReadonly" is set to "false"        }        function foo(arg: Foo) {}      `,      options: [        {          treatMethodsAsReadonly: false,        },      ],    },

Additional info is that before the fix,#privateMember throws an exception whentreatMethodsAsReadonly: true, but no exception and test fails ontreatMethodsAsReadonly: false

After the fix, whether private method withtreatMethodsAsReadonly is set true/false or private field withreadonly or not. There is no exception thrown.

Here are some results of after the fix

{  code: `    class Foo {      #privateField1 = 'foo';     ----> test fails becuase it is not readonly      readonly #privateField2 = 'foo';     -----> test passed because it's readonly      private privateField3 = 'foo';     ----> test fails becuase it's not readonly      private  readonly privateField4 = 'foo';     ----> test passed because it's readonly            #privateMember1() {}     ----> test fails because "treatMethodsAsReadonly" is set false      private #privateMember2() {}   ----> same as above      private privateMember3() {}  ----> same as above    }    function foo(arg: Foo) {}  `,  options: [    {      treatMethodsAsReadonly: false,     },    ],},

@lonyele
Copy link
ContributorAuthor

lonyele commentedJan 1, 2022
edited
Loading

wow... how newbie mistake it is. Actually I didn't even know there is this pending system exists in github. holy cow. though kind of weird that I also checked in incognito mode whether it was actually published... anyway...

bradzacher reacted with thumbs up emoji

@lonyelelonyeleforce-pushed thefix/handle-private-identifier branch fromb63de76 to43e34e4CompareJanuary 1, 2022 07:35
@bradzacherbradzacher added awaiting responseIssues waiting for a reply from the OP or another party and removed awaiting responseIssues waiting for a reply from the OP or another party labelsJan 11, 2022
Copy link
Member

@JoshuaKGoldbergJoshuaKGoldberg left a comment
edited
Loading

Choose a reason for hiding this comment

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

Agree with bradzacher here. Thanks for the discussion + PR so far! ❤️

Would like to see some tests of this behavior too please :)

lonyele reacted with thumbs up emoji
@lonyelelonyeleforce-pushed thefix/handle-private-identifier branch from43e34e4 tocf9199fCompareFebruary 11, 2022 20:50
Comment on lines 225 to 239
// PrivateIdentifier is handled without throwing error.
{
code: `
class Foo {
readonly #privateField = 'foo';
#privateMember() {}
}
function foo(arg: Foo) {}
`,
options: [
{
treatMethodsAsReadonly: true,
},
],
},
Copy link
ContributorAuthor

@lonyelelonyeleFeb 12, 2022
edited
Loading

Choose a reason for hiding this comment

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

Thanks for the playground. I didn't know about that. I just enjoy all your comments on any issues or prs. I think very constructive(very educational to me) :D

please check thiscommit

Comment on lines 160 to 165
const name = ts.getNameOfDeclaration(property.valueDeclaration);
const isPrivateIdentifier = name && ts.isPrivateIdentifier(name);
if (isPrivateIdentifier) {
continue;
}

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

It seems like there is no handy function for checkingprivate identifier from ts or tsutils. I borrowed from atypescript's src/compiler/checker.ts that suits this situation.

 function checkIndexConstraintForProperty(type: Type, prop: Symbol, propNameType: Type, propType: Type) {    const declaration = prop.valueDeclaration;    const name = getNameOfDeclaration(declaration);    if (name && isPrivateIdentifier(name)) {        return;    }    ........}

@lonyele
Copy link
ContributorAuthor

lonyele commentedFeb 12, 2022
edited
Loading

@JoshuaKGoldberg Thanks! just to double-check, what does it mean bysome tests of this behavior? add more tests atprefer-readonly-parameter-types.test.ts or somewhere else?(codecov?)

@bradzacherbradzacher removed the awaiting responseIssues waiting for a reply from the OP or another party labelFeb 12, 2022
@JoshuaKGoldberg
Copy link
Member

some tests of this behavior

What I mean is, Codecov is pointing out that there is at least one line inisTypeReadonly.ts that isn't covered by unit tests. So there should be some unit test(s) somewhere that run that line of code to make sure it works & stays working.

I think there are a couple of options you could go with:

Does that help@lonyele?

@JoshuaKGoldbergJoshuaKGoldberg added the awaiting responseIssues waiting for a reply from the OP or another party labelFeb 12, 2022
@lonyele
Copy link
ContributorAuthor

@JoshuaKGoldberg Absolutely! thanks for the clear guide!

JoshuaKGoldberg reacted with heart emoji

@bradzacherbradzacher removed the awaiting responseIssues waiting for a reply from the OP or another party labelFeb 14, 2022
Copy link
Member

@JoshuaKGoldbergJoshuaKGoldberg left a comment

Choose a reason for hiding this comment

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

Source code looks good to me in general (alas, TypeScript API woes...) but missing a crashing test case from the issue.

@JoshuaKGoldbergJoshuaKGoldberg added the awaiting responseIssues waiting for a reply from the OP or another party labelFeb 23, 2022
Copy link
Member

@JoshuaKGoldbergJoshuaKGoldberg left a comment
edited
Loading

Choose a reason for hiding this comment

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

Super, thanks for all your work on this@lonyele! 🙌

Since@bradzacher also reviewed I'll wait a bit to give them a chance to re-review too.

lonyele reacted with hooray emoji
@bradzacherbradzacher removed the awaiting responseIssues waiting for a reply from the OP or another party labelMar 1, 2022
@bradzacher
Copy link
Member

LGTM! Thanks!

lonyele reacted with hooray emoji

@bradzacherbradzacher merged commita65713a intotypescript-eslint:mainMar 1, 2022
@lonyelelonyele deleted the fix/handle-private-identifier branchMarch 1, 2022 04:45
crapStone pushed a commit to Calciumdibromid/CaBr2 that referenced this pull requestMar 8, 2022
This PR contains the following updates:| Package | Type | Update | Change ||---|---|---|---|| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint) | devDependencies | minor | [`5.13.0` -> `5.14.0`](https://renovatebot.com/diffs/npm/@typescript-eslint%2feslint-plugin/5.13.0/5.14.0) || [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint) | devDependencies | minor | [`5.13.0` -> `5.14.0`](https://renovatebot.com/diffs/npm/@typescript-eslint%2fparser/5.13.0/5.14.0) |---### Release Notes<details><summary>typescript-eslint/typescript-eslint (@&#8203;typescript-eslint/eslint-plugin)</summary>### [`v5.14.0`](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#&#8203;5140-httpsgithubcomtypescript-eslinttypescript-eslintcomparev5130v5140-2022-03-07)[Compare Source](typescript-eslint/typescript-eslint@v5.13.0...v5.14.0)##### Bug Fixes-   **eslint-plugin:** \[naming-convention] cover case that requires quotes ([#&#8203;4582](typescript-eslint/typescript-eslint#4582)) ([3ea0947](typescript-eslint/typescript-eslint@3ea0947))-   **eslint-plugin:** \[no-misused-promises] factor thenable returning function overload signatures ([#&#8203;4620](typescript-eslint/typescript-eslint#4620)) ([56a09e9](typescript-eslint/typescript-eslint@56a09e9))-   **eslint-plugin:** \[prefer-readonly-parameter-types] handle class sharp private field and member without throwing error ([#&#8203;4343](typescript-eslint/typescript-eslint#4343)) ([a65713a](typescript-eslint/typescript-eslint@a65713a))-   **eslint-plugin:** \[return-await] correct autofixer in binary expression ([#&#8203;4401](typescript-eslint/typescript-eslint#4401)) ([5fa2fad](typescript-eslint/typescript-eslint@5fa2fad))##### Features-   **eslint-plugin:** \[no-misused-promises] add granular options within `checksVoidReturns` ([#&#8203;4623](typescript-eslint/typescript-eslint#4623)) ([1085177](typescript-eslint/typescript-eslint@1085177))</details><details><summary>typescript-eslint/typescript-eslint (@&#8203;typescript-eslint/parser)</summary>### [`v5.14.0`](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/parser/CHANGELOG.md#&#8203;5140-httpsgithubcomtypescript-eslinttypescript-eslintcomparev5130v5140-2022-03-07)[Compare Source](typescript-eslint/typescript-eslint@v5.13.0...v5.14.0)**Note:** Version bump only for package [@&#8203;typescript-eslint/parser](https://github.com/typescript-eslint/parser)</details>---### Configuration📅 **Schedule**: At any time (no schedule defined).🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.--- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, click this checkbox.---This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).Co-authored-by: cabr2-bot <cabr2.help@gmail.com>Reviewed-on:https://codeberg.org/Calciumdibromid/CaBr2/pulls/1201Reviewed-by: 6543 <6543@noreply.codeberg.org>Co-authored-by: Calciumdibromid Bot <cabr2_bot@noreply.codeberg.org>Co-committed-by: Calciumdibromid Bot <cabr2_bot@noreply.codeberg.org>
@github-actionsgithub-actionsbot locked asresolvedand limited conversation to collaboratorsMay 25, 2022
Sign up for freeto subscribe to this conversation on GitHub. Already have an account?Sign in.
Reviewers

@bradzacherbradzacherbradzacher left review comments

@JoshuaKGoldbergJoshuaKGoldbergJoshuaKGoldberg approved these changes

Assignees
No one assigned
Labels
bugSomething isn't working
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

[prefer-readonly-parameter-types] crashing with error Non-null Assertion Failed: Expected to find a property "#promise" for the type.
3 participants
@lonyele@bradzacher@JoshuaKGoldberg

[8]ページ先頭

©2009-2025 Movatter.jp