ESLint: Ban @ts-ignore in favour of @ts-expect-error - #81148
Conversation
`@ts-ignore` keeps silently passing once the error it was added for is gone, leaving dead suppressions behind. Enable `@typescript-eslint/ban-ts-comment` so `@ts-ignore` and `@ts-nocheck` are errors, and `@ts-expect-error` requires a description. The rule only walks the comment list, so it applies to JSDoc-typed `.js` files as well as `.ts`/`.tsx`. Existing violations are fixed in follow-up commits.
The `@wordpress/preferences` package is typed now, so these suppressions no longer suppress anything. Confirmed via `tsc --build`, which reports them as unused `@ts-expect-error` directives once converted.
…ect-error` Convert the live suppressions and describe what each one covers. Drop the ones `tsc` reports as unused, which no longer suppress anything.
Describe each live suppression with the reason `tsc` actually reports, and drop the directives that no longer suppress anything. Also corrects the Boot and Blocks descriptions from the previous commit.
…s-ignore` Describe each live suppression with the reason `tsc` reports, and drop the ones that suppress nothing. `tools/` and `routes/` belong to no TypeScript project, so their directives were checked against a temporary project built on `tsconfig.base.json`.
Describe each remaining suppression and drop the ones that suppress nothing. Test directories excluded from the build were checked against a temporary project so the descriptions match a real diagnostic.
`@types/react@18` does not declare `inert` at all, so saying its types do not accept the string form wrongly implies a boolean form works.
Several descriptions named the wrong cause: the failing overload in `rtl.js` is a missing index signature on `CSSProperties`, the story layout error is union narrowing rather than an unknown property, and the DataViews test error comes from the spread `onClick` handler. Four directives in the Upload Media tests suppressed nothing, since the preceding `in` guard already narrows the object. Test descriptions now name the type that is violated rather than only stating that the input is deliberately invalid.
|
Size Change: 0 B Total Size: 7.81 MB |
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
Flaky tests detected in 99ac5c3. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/30903043886
|
aduth
left a comment
There was a problem hiding this comment.
This is great to keep our disabling truthful to the code 👍
I left some comments about specific instances as I was reviewing them. I don't expect those to be addressed here; in fact, I'd prefer they weren't addressed here, and I understand the backfilled descriptions to justify their existence as intended at the time. I'm hopeful that requiring descriptions will push against the instinct to add disabling comments when a better solution might exist.
| * External dependencies | ||
| */ | ||
| // @ts-ignore | ||
| // @ts-expect-error `hpq` does not ship type declarations. |
| if ( isValidElement( icon ) ) { | ||
| return cloneElement( icon, { | ||
| // @ts-ignore Just forwarding the size prop along | ||
| // @ts-expect-error `size` is forwarded but is not in the icon component overloads. |
There was a problem hiding this comment.
This code is a little odd, both in terms of whether it even makes sense to pass through all these props blindly, or if we could be doing a better job of typing it such that we don't need to disable. Cursory review with AI shows it might be possible but not entirely straight-forward.
| // @ts-expect-error `cursor` is typed as `string`, but `null` clears it. | ||
| document.documentElement.style.cursor = null; |
There was a problem hiding this comment.
This looks like case where we're "doing it wrong" and could do this in a type-compliant way.
| // @ts-expect-error `cursor` is typed as `string`, but `null` clears it. | |
| document.documentElement.style.cursor = null; | |
| document.documentElement.style.cursor = ''; |
Setting to empty string removes the proeprty.
Source:
If value is the empty string, invoke removeProperty() with property as argument and return.
https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setproperty
Setting to null is the same as setting to empty string.
Source:
Instead of being stringified to "null", which is the default, [a JavaScript
null] will be converted to the empty string.
https://webidl.spec.whatwg.org/#LegacyNullToEmptyString
| // @ts-expect-error `cursor` is typed as `string`, but `null` clears it. | |
| document.documentElement.style.cursor = null; | |
| document.documentElement.style.removeProperty( 'cursor' ); |
Ref: https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration/removeProperty
| className="dataviews-footer" | ||
| // @ts-ignore | ||
| // @ts-expect-error `inert` is not declared in React 18's HTML attribute types. | ||
| inert={ isRefreshing ? 'true' : undefined } |
There was a problem hiding this comment.
We should think about how to support this in a cross-React 18/19 compatible way (cc @jsnajdr ).
| value: filterInView.value, | ||
| // @ts-ignore | ||
| // @ts-expect-error `label` is a `string[]` here, but the element type expects a `string`. | ||
| label, |
There was a problem hiding this comment.
This seems like it might just be incorrect typings on label ?
It's typed as a string:
But then it's referenced as an array of two members, as we're constructing here:
gutenberg/packages/dataviews/src/utils/operators.tsx
Lines 230 to 231 in ad0af52
I guess it's a little tricky, because other places treat it as a string, it's just this "between" operator that's doing something unique. string | string[] could work as a type, but it overpromises for those other cases.
| const visibleLockedFields = lockedFields.filter( | ||
| ( { isVisibleFlag } ) => | ||
| // @ts-expect-error | ||
| // @ts-expect-error A string key cannot index `View`. |
There was a problem hiding this comment.
This could probably be addressed by changing the typing above for isVisibleFlag: string to isVisibleFlag: 'showTitle' | 'showMedia' | 'showDescription' or some variation of keyof typeof view that reflects the fields we're expecting.
| !! blockType?.supports?.__experimentalLayout; | ||
| const fallbackGapValue = | ||
| // @ts-expect-error | ||
| // @ts-expect-error `blockGap` support is typed as `boolean | AxialDirection[]`. |
There was a problem hiding this comment.
Maybe we should update the types to include __experimentalDefault ? We could debate whether to add types for experimental attributes, but there's plenty of precedent already in the same file.
| // @ts-expect-error `backgroundImage` is a union whose other members have no `url`. | ||
| ! backgroundStyle?.backgroundImage?.url |
There was a problem hiding this comment.
We could probably address this with effective narrowing / type predicates.
Yes, my intension was to keep this PR free from any runtime changes and focus purely on getting the rule into trunk.
My opinion is that the forced description will make people/agents think about the justification and may be that can help the root cause. |
|
@aduth I am merging this as is and then I will create a follow up PR or two to adress the concerns. |
hpq 1.4.0 ships TypeScript declarations, so the two suppressions added in #81148 are no longer needed. Type the call sites against the new declarations.
|
I have created the following PRs as follow ups to fix the easy ones here |
…typing (#81200) * Components: Use removeProperty to clear the drag cursor Setting `style.cursor = null` relied on WebIDL's LegacyNullToEmptyString coercion and needed a `@ts-expect-error`. `removeProperty( 'cursor' )` does the same thing in a type-compliant way. * Types: Replace remaining input-control, icon, dataviews and styles ts-expect-errors Follow-up to review feedback on #81148: type each of these sites so the suppression is unnecessary rather than describing why it was needed. - Icon: type the cloned element's props as the sizing props it forwards. - Properties section: key `isVisibleFlag` to the `View` flags it indexes. - Block supports: type the object form of `spacing.blockGap`. - Background: narrow `backgroundImage` with a type predicate. Co-authored-by: manzoorwanijk <manzoorwanijk@git.wordpress.org> Co-authored-by: Mamaduka <mamaduka@git.wordpress.org>
What?
Enables
@typescript-eslint/ban-ts-commentso@ts-ignoreis an error and@ts-expect-errorrequires a description, then fixes every existing violation.Why?
@ts-ignorekeeps silently passing once the error it was added for is gone, so stale suppressions accumulate unnoticed.@ts-expect-errorfails when it is no longer needed, which surfaces them.That is exactly what happened here: of the 117
@ts-ignorecomments ontrunk, 35 (30%) were stale — they suppressed nothing. Several also carried reasons that were no longer true, such as// @ts-ignore - Preferences package is not typedwhen@wordpress/preferenceshas been typed for a while.Follow up to #80832 (comment).
How?
The rule is added to the repo config in
tools/eslint/config.mjs. Defaults are kept, so@ts-expect-errorneeds a description of at least 3 characters. It applies to.jsas well as.ts/.tsx— the rule only walks the comment list, so it works under the Babel parser too.The 177 violations were resolved as:
@ts-ignoreconverted to@ts-expect-errorwith a description@ts-ignoredeleted as stale@ts-expect-errorgiven a descriptionEach description states the reason TypeScript actually reports, taken from the compiler rather than guessed. Every directive was temporarily neutralised and
tsc --buildre-run to capture the real diagnostic, then restored.Some paths get no signal from the build:
tsconfig.base.jsonexcludes**/test/**, andtools/androutes/are not in any TypeScript project. Directives there were checked against a temporary project extendingtsconfig.base.json, and againstlib.dom.d.tsand package metadata where that was the relevant fact.Unrelated drive-by: one pre-existing
import/ordererror inpackages/components/src/button/test/index.tsxis fixed, because the pre-commit hook lints staged files under the strict config and it blocked the commit.Testing Instructions
npm run lint:jspasses.npx tsc --buildfrom the repo root exits cleanly.// @ts-ignoreanywhere and confirm linting fails with a message pointing at@ts-expect-error.// @ts-expect-errorand confirm linting asks for a description.Use of AI Tools
Authored with Claude Code, then audited with Codex specifically for the factual accuracy of the new comments. That audit caught descriptions that named the wrong cause and four directives that suppressed nothing; each finding was re-verified against real
tscoutput before being applied. I have reviewed and take responsibility for the changes.