Skip to content

Convert the entire codebase to TypeScript#5549

Merged
mstange merged 41 commits intofirefox-devtools:mainfrom
mstange:typescript-migration
Aug 14, 2025
Merged

Convert the entire codebase to TypeScript#5549
mstange merged 41 commits intofirefox-devtools:mainfrom
mstange:typescript-migration

Conversation

@mstange
Copy link
Contributor

@mstange mstange commented Aug 8, 2025

FIxes #2931.

I squashed and re-split the changes from #5531.

yarn test-all passes after every commit.

702 files changed, 13512 insertions(+), 64132 deletions(-)

@mstange mstange requested a review from canova as a code owner August 8, 2025 03:54
@codecov
Copy link

codecov bot commented Aug 8, 2025

Codecov Report

❌ Patch coverage is 87.79586% with 330 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.84%. Comparing base (333baf8) to head (16260f2).

Files with missing lines Patch % Lines
src/actions/receive-profile.ts 87.14% 68 Missing ⚠️
src/actions/profile-view.ts 91.93% 50 Missing and 2 partials ⚠️
src/app-logic/url-handling.ts 90.25% 48 Missing and 1 partial ⚠️
src/components/app/CodeErrorOverlay.tsx 0.00% 32 Missing ⚠️
src/actions/app.ts 75.23% 26 Missing ⚠️
src/app-logic/browser-connection.ts 73.84% 17 Missing ⚠️
src/components/app/BottomBox.tsx 70.90% 13 Missing and 3 partials ⚠️
src/components/app/AppViewRouter.tsx 79.59% 10 Missing ⚠️
src/actions/code.ts 50.00% 8 Missing ⚠️
src/app-logic/web-channel.ts 89.18% 8 Missing ⚠️
... and 12 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5549      +/-   ##
==========================================
- Coverage   86.13%   85.84%   -0.30%     
==========================================
  Files         309      309              
  Lines       29883    30319     +436     
  Branches     8038     8345     +307     
==========================================
+ Hits        25740    26026     +286     
- Misses       3552     3872     +320     
+ Partials      591      421     -170     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@mstange mstange force-pushed the typescript-migration branch 8 times, most recently from 60d093b to 454f419 Compare August 8, 2025 04:36
package.json Outdated
"flow-stop": "flow stop",
"flow-coverage": "npx flow-coverage-report -i 'src/**/*.js' -t html -t text",
"flow-generate-libdefs": "npx flow-typed install --libdefDir src/types/libdef",
"typecheck": "tsc --noEmit",
Copy link
Member

Choose a reason for hiding this comment

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

nit: can we maybe name this ts? yarn ts is shorter and easier to type for me. And we already have the same command in profiler-server.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Absolutely!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've added a commit to do the rename.

@mstange mstange force-pushed the typescript-migration branch 3 times, most recently from 9b9eeee to 16260f2 Compare August 11, 2025 21:39
Copy link
Member

@canova canova left a comment

Choose a reason for hiding this comment

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

Oh wow, this PR is huge, amazing work! I couldn't finish the review yet, I'm at ecff67d (Convert window-console.js and index.js.) at the moment. At least all the shipping code is done! Tomorrow I will continue with the rest. I'm submitting the comments that I wrote so far, so you can look at my them sooner than later. I expect to finish the review tomorrow.

I see a few of places where I see flow being mentioned. But I think they can be removed as a follow-ups.

For example:

  • We still have utils/flow.ts. It's funny that it's flow.ts now, maybe we can change it to utils/typescript.ts or utils/type.ts
  • I see some flow mentions inside src/reducers/README.md
  • Some more flow mentions in the comments.

But they all could be done as follow-ups.

Some files for some reason don't appear they are git mved even though they are in the same commit, but that's fine I think.

Also as a follow-up, it might be good to add the commit hashes of these inside .git-blame-ignore-revs so they would be ignored from the blame? (don't know if adding the merge commit hash is enough or we need to add every one of them individually)

Ah lastly, can you run npx yarn-deduplicate to deduplicate some of the packages that we added?

package.json Outdated
"flow-coverage": "npx flow-coverage-report -i 'src/**/*.js' -t html -t text",
"flow-generate-libdefs": "npx flow-typed install --libdefDir src/types/libdef",
"protoc": "npx -p protobufjs-cli pbjs -t static-module -w commonjs -o ./src/profile-logic/import/proto/simpleperf_report.js ./src/profile-logic/import/proto/simpleperf_report.proto",
"ts": "tsc --noEmit",
Copy link
Member

Choose a reason for hiding this comment

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

We already have noEmit: true inside tsconfig.json, we can remove this one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, yes.

tsconfig.json Outdated

// Exceptions to strictness, consider enabling in the future
"useUnknownInCatchVariables": false, // Sounds fine, enforces `} catch (e) { if (e instanceof Error) {` etc
"alwaysStrict": false, // Affects runtime behavior, I think - need to check what we had with Flow
Copy link
Member

Choose a reason for hiding this comment

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

Doc says: Ensures that your files are parsed in the ECMAScript strict mode, and emit “use strict” for each source file.

Since we have "noEmit": true, we are not actually using tsc itself for transpiling the source into JS, and rely on babel. So I think this option shouldn't have any effect.

I actually removed this line and compiled the source code again and main js bundle file has the same hash.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah of course, makes sense. I'll remove it.

// Exceptions to strictness, consider enabling in the future
"useUnknownInCatchVariables": false, // Sounds fine, enforces `} catch (e) { if (e instanceof Error) {` etc
"alwaysStrict": false, // Affects runtime behavior, I think - need to check what we had with Flow
"exactOptionalPropertyTypes": false, // Benefit unclear
Copy link
Member

Choose a reason for hiding this comment

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

Hm, I'm not so sure about this, removing it doesn't throw any error, but also don't know if it makes much difference.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, it seems to default to false even in "strict": true mode, so removing this line doesn't enable the flag.

tsconfig.json Outdated

// Module Resolution
"resolveJsonModule": true,
"baseUrl": ".",
Copy link
Member

Choose a reason for hiding this comment

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

Do we need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

seems not, I'll remove it.

low?: number,
high?: number
low: number = 0,
high: number = array.length
Copy link
Member

Choose a reason for hiding this comment

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

Whattt, I don't know why I didn't know this, but I've never tried referencing another parameter inside the following parameters in JS before (like = array.length). I just assumed it wouldn't work. I guess? TIL!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Haha yes, this is one of the few times where this is quite handy!

Comment on lines 64 to 66
getApp(state).trackThreadHeights as unknown as {
[key: ThreadsKey]: CssPixels;
};
Copy link
Member

Choose a reason for hiding this comment

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

nit: Removing the casting still passed the ts check for me:

export const getTrackThreadHeights: Selector<{
  [key: ThreadsKey]: CssPixels;
}> = (state) => getApp(state).trackThreadHeights;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oh thanks for catching that

});
dispatch({
type: 'CHANGE_SELECTED_TAB',
type: 'CHANGE_SELECTED_TAB' as const,
Copy link
Member

Choose a reason for hiding this comment

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

oh curious, why do we need to pass them as const?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hmm, it seems I don't need to! Maybe I converted this file before I had the Action type properly set up.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've removed the as consts.

|};

type Props = {|
...RestProps,
Copy link
Member

Choose a reason for hiding this comment

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

Is it not possible to use ...RestProps, in ts? Hm, I see & SizeProps in some other places.

Copy link
Contributor Author

@mstange mstange Aug 14, 2025

Choose a reason for hiding this comment

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

The trouble here was the sampleIndex prop, which is | null for Props but required in RestProps.
If I do type Props = { <nonrestprops> } & RestProps thensampleIndex also becomes required in Props.

'TrackPower--tooltip-power-watt',
'TrackPower--tooltip-power-milliwatt'
'TrackPower--tooltip-power-milliwatt',
'TrackPower--tooltip-power-microwatt'
Copy link
Member

Choose a reason for hiding this comment

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

Ah, I guess we didn't have this TrackPower--tooltip-power-microwatt l10n id before. It would be good file a bug for that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oops, well, we still don't have it. Filed #5554 and removed the change. I changed the type of the last argument to _formatPower to be optional.

Copy link
Member

@canova canova left a comment

Choose a reason for hiding this comment

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

I'm done going through the commits! It was easier to look at the second half.

Wow, thanks a lot for the migration! Looks great. Let's land this and we can follow-up with the improvements.

FWIW, I was using this branch locally to test stuff too and I can really feel the difference between flow and this! It was taking a long time to get LSP errors on my editor with flow, now I'm getting near instant feedback on errors! The experience difference is night and day. And after this, I can finally remove every config/plugin related to Flow from my editor.

* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// @flow
// @ts-nocheck Complex window/history mock with extensive DOM API manipulation that requires intricate typing
Copy link
Member

Choose a reason for hiding this comment

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

Not really worth a look now, but it could be interesting to see if we can do it somehow (not for this PR, some other time)

export function fireFullKeyPress(
element: HTMLElement,
options: { key: string, ... }
options: { key: string }
Copy link
Member

Choose a reason for hiding this comment

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

Heh non exact typing comes in handy here.

Comment on lines +30 to +57
/**
* These type tests create various values that should all type check correctly to show
* that the react-redux system is working correctly.
*
* We are catching the following mistakes at the type level:
*
* explicitConnect plain connect
* mapStateToProps:
* - missing prop yes yes
* - prop with wrong type yes yes
* - extra prop no (was yes with Flow) no
*
* mapDispatchToProps:
* - missing prop yes yes
* - action wrong type yes yes
* - thunk action wrong type yes yes
* - extra prop no (was yes with Flow) no
*
* OwnProps:
* - missing prop yes yes
* - prop with wrong type yes yes
* - extra prop yes yes
*
* In summary, we lost the ability to detect unused state props and unused
* dispatch props when we migrated to TypeScript. On the other hand,
* explicitConnect now catches the same mistakes as plain connect, so we
* can migrate to plain connect at any time without losing type coverage.
*/
Copy link
Member

Choose a reason for hiding this comment

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

Thanks for the comment! I guess that was the reason you mentioned #3063?

expect(Array.from(set)).toEqual(sampleValues);
expect(Array.from(set.values())).toEqual(sampleValues);
// @ts-expect-error The TypeScript definition of Array.from only accepts Iterable, not Iterator - not sure what's correct here
expect(Array.from<CallNodePath>(set.values())).toEqual(sampleValues);
Copy link
Member

Choose a reason for hiding this comment

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

Huh, that's weird, but I have no idea either.

const matchUrlResult = fetchUrlRe.exec(url);
if (matchUrlResult) {
// $FlowExpectError Our Flow doesn't know about named groups.
Copy link
Member

Choose a reason for hiding this comment

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

🎉

Copy link
Member

Choose a reason for hiding this comment

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

Heh, I agree that this doesn't look like a correct location for this test (not for this PR again ofc).

mstange added 13 commits August 13, 2025 21:30
I had to add two "resolutions" to the package.json:

- "@types/react-splitter-layout/@types/react": "^18.3.1" so that we don't end
  up with two different typings for React.
- "@types/trusted-types": "^2.0.7" because workbox depends on "^2.0.2" which
  has conflicts with the "official" TypeScript DOM types, and this conflict
  was fixed in v2.0.3. v2.0.7 is just the most recent version, so I picked that.
@mstange mstange force-pushed the typescript-migration branch from 16260f2 to 8ba9ac4 Compare August 14, 2025 18:42
@mstange mstange force-pushed the typescript-migration branch from 8ba9ac4 to fe67997 Compare August 14, 2025 18:48
@mstange
Copy link
Contributor Author

mstange commented Aug 14, 2025

Thanks for the thorough review of this huge PR!

I see a few of places where I see flow being mentioned. But I think they can be removed as a follow-ups.

I ended up fixing them all.

For example:

* We still have `utils/flow.ts`. It's funny that it's flow.ts now, maybe we can change it to utils/typescript.ts or utils/type.ts

Renamed to utils/types.ts. I don't like the file very much - I think files under utils should be the "leaves" of the module dependency tree, and not depend on type definitions for our actions etc. I think functions like toValidTabSlug should be close to where we define the TabSlug type, etc.

* I see some flow mentions inside src/reducers/README.md

Fixed, and I went through the other .md files as well.

* Some more flow mentions in the comments.

Oh actually this one I haven't fixed.

Some files for some reason don't appear they are git mved even though they are in the same commit, but that's fine I think.

Yeah I'm not sure why. Maybe because those didn't hit the percentage threshold of preserved lines.

Also as a follow-up, it might be good to add the commit hashes of these inside .git-blame-ignore-revs so they would be ignored from the blame? (don't know if adding the merge commit hash is enough or we need to add every one of them individually)

Ah yes, good idea.

Ah lastly, can you run npx yarn-deduplicate to deduplicate some of the packages that we added?

Done.

FWIW, I was using this branch locally to test stuff too and I can really feel the difference between flow and this! It was taking a long time to get LSP errors on my editor with flow, now I'm getting near instant feedback on errors! The experience difference is night and day. And after this, I can finally remove every config/plugin related to Flow from my editor.

🎉

@mstange mstange merged commit 44b7b85 into firefox-devtools:main Aug 14, 2025
13 checks passed
@canova canova mentioned this pull request Sep 2, 2025
canova added a commit that referenced this pull request Sep 2, 2025
Changes:

[Nazım Can Altınova] Display the marker description at the top inside
the marker tooltips (#5534)
[Florian Quèze] Change the 'JavaScript' radio button label to 'Script'
(#5530)
[Markus Stange] Implement profile logic and some selectors for the
function list (#5525)
[Markus Stange] Some small type fixes (#5538)
[Markus Stange] Simplify return type of the callback we pass to
setState. (#5540)
[Markus Stange] Pass the correct value to the reducer's action argument
(#5543)
[Markus Stange] Change withSize to accept PropsWithoutSize as its type
parameter (#5541)
[Nazım Can Altınova] Make sure that the test-debug command runs the
tests properly (#5545)
[Markus Stange] Improve type coverage involving network phases (#5539)
[Markus Stange] Change implementation of withChartViewport (#5542)
[Florian Quèze] A new permalink should be generated and shown after
using the re-upload feature. (#5547)
[Florian Quèze] Show the vertical ruler in the timeline when hovering
the network chart (#5548)
[Markus Stange] Convert the entire codebase to TypeScript (#5549)
[Nazım Can Altınova] Update the yarn.lock file after recent changes
(#5557)
[Markus Stange] Add proper TypeScript coverage for window-navigation.ts
(#5559)
[Markus Stange] Remove leftover $FlowExpectError comments (#5560)
[Markus Stange] Fix Iterator / Iterable confusion (#5561)
[Nazım Can Altınova] Remove the unneeded test-all:ci script (#5566)
[Nazım Can Altınova] Fix a type case inconsistency (#5569)
[Florian Quèze] Make 'yarn lint --fix' work as an alias to 'yarn
lint-fix'. (#5563)
[Ryan Hunt] Don't stringify JSON again in fetchUrlResponse (#5570)
[Nazım Can Altınova] Upgrade ESLint to version 9 (#5567)
[Markus Stange] Simplify Worker setup, and support .json.gz inputs in
symbolicator-cli (#5556)
[Nazım Can Altınova] Add TypeScript coverage to the intersection
observer mock (#5574)
[Markus Stange] Set the preview selection to null when there is no
selection (#5568)
[Markus Stange] Add tests for query-api.ts (#5571)
[Markus Stange] Enable noUnusedParameters and
@typescript-eslint/no-unused-vars and clean up a few more things (#5576)
[Ryan Hunt] Embed iongraph-web and use for iongraph.json source files
(#5577)
[Markus Stange] Remove recursion in filterThreadToSearchString (#5572)


And thanks to our localizers:

be: Mikalai Udodau
de: Michael Köhler
el: Jim Spentzos
en-CA: chutten
en-GB: Paul
es-CL: ravmn
fr: Théo Chevalier
fur: Fabio Tomat
fy-NL: Fjoerfoks
ia: Melo46
it: Francesco Lodolo [:flod]
nl: Mark Heijl
pt-BR: Marcelo Ghelman
ru: Valery Ledovskoy
sv-SE: Luna Jernberg
tr: Rua
tr: Selim Şumlu
uk: Іhor Hordiichuk
zh-CN: Olvcpr423
zh-TW: Pin-guang Chen
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consider migrating from Flow to TypeScript

2 participants