Skip to content

Fix all $TSFixMe instances - #6450

Merged
remcohaszing merged 6 commits into
mainfrom
fix-ts-fix-me
Aug 13, 2026
Merged

Fix all $TSFixMe instances#6450
remcohaszing merged 6 commits into
mainfrom
fix-ts-fix-me

Conversation

@remcohaszing

Copy link
Copy Markdown
Member

There are some slight logic changes because of issues revealed by the new type errors that surfaced.

Also some type errors have now been suppressed and need a closer look later.

There are some slight logic changes because of issues revealed by the
new type errors that surfaced.

Also some type errors have now been suppressed and need a closer look
later.
@changeset-bot

changeset-bot Bot commented Aug 7, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 260812e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@uppy/screen-capture Minor
@uppy/dashboard Minor
@uppy/core Minor
uppy Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@mifi mifi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I couldn't find issues myself while reviewing manually, but I ran it through AI and I think I agree with many of the points. Could you take a look? Most important are the "blocking" points. Be sure to test those in a browser to confirm that they are not regressions.

Overview

Replaces the type $TSFixMe = any escape hatch across @uppy/dashboard, @uppy/screen-capture, and one line of @uppy/core with real types: proper props interfaces for ~12 Preact components, typed DOM utils, and a UploadState union. The PR description is upfront that some logic changed and some errors were suppressed rather than fixed.

Genuinely good parts: copyToClipboard and createSuperFocus are cleaner, getUploadingState's as const union is the right call, FOCUSABLE_ELEMENTS.join(',') is exactly equivalent to the old array→string coercion, and dropping the isSizeMD prop is correct — I grepped, nothing consumed it.

But three changes alter runtime behavior in ways the type-only framing hides, and two of them look like regressions.


Blocking

1. ignoreEvent: ev.targetev.currentTarget breaks paste into inputs — [utils/ignoreEvent.ts](packages/@uppy/dashboard/src/utils/ignoreEvent.ts)

This handler is attached to container <div>s ([FileCard/index.tsx:114-117](packages/@uppy/dashboard/src/components/FileCard/index.tsx:114), [PickerPanelContent.tsx:22-25](packages/@uppy/dashboard/src/components/PickerPanelContent.tsx:22)), so currentTarget is always that div. The tagName === 'INPUT' || 'TEXTAREA' branch becomes dead code and every paste/drop now falls through to ev.preventDefault(). The file's own header comment states the intent — ignore events unless they're in an input — so target is the correct property. Practical effect: pasting a URL into the Url plugin's input or typing/pasting into FileCard meta fields gets cancelled. Revert to ev.target, typed as TargetedEvent<Element> with a (ev.target as Element).tagName read (or EventTarget & { tagName?: string }).

2. CopyLinkButton: event.currentTarget is null by the time the .then() runs — [FileItem/Buttons/index.tsx:105](packages/@uppy/dashboard/src/components/FileItem/Buttons/index.tsx:105)

.then(() => event.currentTarget.focus({ preventScroll: true }))

Per the DOM spec currentTarget is only set during dispatch and reset to null afterwards; Preact uses native listeners with no event pooling, so this callback — a microtask after the handler returns — reads null and throws. Since it's chained after .catch(uppy.log), it surfaces as an unhandled rejection and the "avoid losing focus" behavior is lost. event.target survives dispatch, which is why the original worked. Capture it before the async chain instead:

const button = event.currentTarget
// …then: .then(() => button.focus({ preventScroll: true }))

3. activePickerPanel: Target | undefinedTarget makes the type lie — [Dashboard.tsx:101](packages/@uppy/dashboard/src/Dashboard.tsx:101)

The runtime still assigns undefined in two places ([:346](packages/@uppy/dashboard/src/Dashboard.tsx:346) and [:1346](packages/@uppy/dashboard/src/Dashboard.tsx:1346)). DashboardState is an exported interface, so consumers now get a non-nullable type for a value that is undefined most of the time — and the NonNullable<…> wrappers added in the consumers are silently no-ops. The narrowing at [components/Dashboard.tsx:332](packages/@uppy/dashboard/src/components/Dashboard.tsx:332) is what actually makes the render site safe. Keep | undefined on the state type; the NonNullable<> on the prop types is then meaningful and correct.


Should address

4. Duplicate file-editor:cancel module augmentation — [EditorPanel.tsx:12-16](packages/@uppy/dashboard/src/components/EditorPanel.tsx:12)

@uppy/image-editor already augments UppyEventMap with this exact event ([ImageEditor.tsx:48](packages/@uppy/image-editor/src/ImageEditor.tsx:48)). The two are structurally identical today so merging compiles, but any drift produces TS2717 ("Subsequent property declarations must have the same type") in user code that imports both plugins — a failure the monorepo's own CI won't catch, since dashboard doesn't depend on image-editor. Dashboard emits the event, so the declaration belongs in @uppy/core next to the other core events, with image-editor's entry removed.

5. Three of the eight new @ts-expect-errors are hiding real bugs with one-line fixes

  • [FileCard/index.tsx:83](packages/@uppy/dashboard/src/components/FileCard/index.tsx:83) — toggleFileCard(false) drops the required fileID. The implementation calls this.uppy.getFile(fileID) before branching on show ([Dashboard.tsx:565](packages/@uppy/dashboard/src/Dashboard.tsx:565)), so cancelling emits dashboard:file-edit-complete with undefined. fileCardFor is in scope — toggleFileCard(false, fileCardFor) fixes it.
  • [MetaErrorMessage.tsx:9](packages/@uppy/dashboard/src/components/FileItem/MetaErrorMessage.tsx:9) — the comment says "This should not be an error", but TS is right: metaFields is MetaField[] | ((file: UppyFile) => MetaField[]) and it's being called with no argument, so a user's callback receives undefined. file is in scope here too; thread it through to metaFieldIdToName. The second suppression on fields.filter then also goes away once metaFields optionality is handled (and field[0].name still needs a guard — it throws on an unknown id).
  • [PickerPanelTopBar.tsx:45,53](packages/@uppy/dashboard/src/components/PickerPanelTopBar.tsx:45) — "TypeScript catches a runtime issue here" is a misread. state is only ever assigned WAITING/PREPROCESSING/POSTPROCESSING (the uploading case returns early), so state !== STATE_UPLOADING is always true. It's dead, not buggy. Delete both clauses — behavior is identical and two suppressions disappear.

The remaining FileCard suppressions all trace to const storedMetaData = {} as M. Keeping the form state as Record<string, string> and casting once at saveFileCard(formState as M, fileCardFor) removes all three.

6. copyToClipboard now actually applies its styles — [utils/copyToClipboard.ts](packages/@uppy/dashboard/src/utils/copyToClipboard.ts)

The old setAttribute('style', {…} as string) stringified to "[object Object]", i.e. no styles: the textarea was appended full-size and select() could scroll the page. The new code is what the original author intended, so this is a fix — but it's a visible behavior change buried in a typing PR and deserves a line in the changeset and a manual check of the copy-link flow. (return resolve()resolve() is inert; nothing follows in the try.)

7. No changeset. Beyond the runtime changes above, this touches exported types (MetaField now exported, DashboardState.activePickerPanel narrowed, getActiveOverlayEl/createSuperFocus signatures) in three published packages. A patch changeset covering @uppy/core, @uppy/dashboard, @uppy/screen-capture is warranted.


Minor

  • [FileCard/index.tsx:51,55](packages/@uppy/dashboard/src/components/FileCard/index.tsx:51) — files[fileCardFor!] keeps a ! on a prop now typed fileCardFor: string. Redundant; drop it.
  • [EditorPanel.tsx:34](packages/@uppy/dashboard/src/components/EditorPanel.tsx:34) — props.files[props.fileCardFor!] on a UppyFileId | null prop. It happens to hold because openFileEditor sets both together ([Dashboard.tsx:396-399](packages/@uppy/dashboard/src/Dashboard.tsx:396)), but fileCardFor: file.id || null admits null. An early if (!file) return null would be honest.
  • [getActiveOverlayEl.ts:11](packages/@uppy/dashboard/src/utils/getActiveOverlayEl.ts:11) — use querySelector<HTMLElement>(…) instead of the trailing as HTMLElement, matching what createSuperFocus/trapFocus now do.
  • [UIPlugin.ts:48](packages/@uppy/core/src/UIPlugin.ts:48) — el!: … el: … = null changes the pre-mount value from undefined to null. No === undefined checks exist on it, so this is fine and arguably better, but it's an observable change on a public plugin base class.
  • The three new this.el! assertions in Dashboard.tsx are the direct cost of that change. if (!this.el) return guards in the two trapFocus callers would be truthful rather than assumed.

Test coverage & security

No tests added, and none of the three runtime changes is covered by the existing suite — which is why CI is green despite items 1 and 2. ignoreEvent and the copy-link focus restoration would both be cheap browser-mode tests and are exactly the kind of regression this PR risks. Nothing security-relevant: no auth, network, or user-input-to-DOM paths change, and copyToClipboard still targets the same execCommand/prompt fallback.

Verdict

The direction is right and most of the file-by-file work is solid — I'd merge it happily once items 1–3 are addressed, since those are behavior regressions rather than typing preferences. Items 4–7 are worth resolving before merge too, but they're arguable. Want me to push fixes for the three blocking items to the branch, or leave this as a review comment on the PR?

@qxprakash qxprakash assigned qxprakash and remcohaszing and unassigned qxprakash Aug 11, 2026
@qxprakash qxprakash added the Types Issues relating to the Typescript definition files label Aug 11, 2026
@remcohaszing

Copy link
Copy Markdown
Member Author

I agree with 1-3, so I resolved them. Quite some issues come from the prop spread pattern <Component {...props} />. This throws off TypeScript. We may want to avoid this and replace most occurrences in the future.

4 is explicitly a good feature, not a bug.

5 is a fair point which I also noticed. These are something someone should have a closer look at who is already more familiar with the code base.

6 is true. Someone more familiar with the code base should have closer look.

7 is resolved

The minor issues are resolved.

mifi and others added 2 commits August 12, 2026 22:44
- FileCard: `toggleFileCard(false)` dropped the required `fileID`, so
  `dashboard:file-edit-complete` was emitted with `undefined` — the
  implementation looks the file up before branching on `show`. Pass
  `fileCardFor` and remove the suppression.
- PickerPanelTopBar: `state` is only ever WAITING/PREPROCESSING/
  POSTPROCESSING because the uploading branch returns early, so both
  `state !== STATE_UPLOADING` checks were always true. Removing them is
  behaviour preserving and drops two suppressions.
- EditorPanel: replace the `fileCardFor!` assertion with an early return,
  since the prop admits `null`.
- FileCard, getActiveOverlayEl: drop a redundant `!` and an `as HTMLElement`.
- Mention the two user visible behaviour changes in the changeset.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mifi

mifi commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

pushed some more fixes. if you agree then i think this can be merged.

@remcohaszing
remcohaszing merged commit 5c15bdf into main Aug 13, 2026
11 checks passed
@remcohaszing
remcohaszing deleted the fix-ts-fix-me branch August 13, 2026 08:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Types Issues relating to the Typescript definition files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants