-
-
Notifications
You must be signed in to change notification settings - Fork 253
Description
Motivation
As part of Shared Libraries Q2 2024 OKRs (O3/KR4), we will be upgrading all core packages to use TypeScript v5.0.
There are some significant blockers associated with this upgrade due to breaking changes in TypeScript's handling of modules. Once unblocked, we will be able to proceed to further version upgrades, gaining access to the latest features and improvements in TypeScript, and reaching parity with the extension.
References
- Blocked by Upgrade to TypeScript v4.9 #4086
Features
constType Parameters:- Causes
as constinference by default. - 74
as constinstances in core.
- Causes
type HasNames = { names: readonly string[] };
function getNamesExactly<const T extends HasNames>(arg: T): T["names"] {
// ^^^^^
return arg.names;
}
// Inferred type without `const`: string[]
// Inferred type with `const`: readonly ["Alice", "Bob", "Eve"]
// Note: Didn't need to write 'as const' here
const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"] });enum:- All
enums are unionenums enumoverhaul- 37
export enuminstances in core.
- All
- Decorators
Blockers
node16/nodenext
-
TypeScript v4.7 introduced the
node16/nodenextoptions for the--moduleand--moduleResolutioncompiler options. -
We are currently at v4.8 using the
nodeoption formoduleResolution, which is renamed tonode10starting from TypeScript v5.0 (thenodealias is maintained for compatibility).node10does not support correct module resolution for ESM packages.node10does not support theexportsfield in package.json
bundler
- v5.0 introduces the
bundleroption for better compatibility, but this isn't intended for usage with npm libraries that don't use a bundler to build and deploy.
On the other hand, if you’re writing a library that’s meant to be published on npm, using the bundler option can hide compatibility issues that may arise for your users who aren’t using a bundler. So in these cases, using the node16 or nodenext resolution options is likely to be a better path.
bundlerrequires--moduleto bees2020+ and looks for type declarations underdist/esm/.
src/AccountsController.test.ts:6:28 - error TS7016: Could not find a declaration file for module '@metamask/snaps-utils'. '/home/runner/work/core/core/node_modules/@metamask/snaps-utils/dist/esm/index.js' implicitly has an 'any' type.
Try `npm i --save-dev @types/metamask__snaps-utils` if it exists or add a new declaration (.d.ts) file containing `declare module '@metamask/snaps-utils';`
6 import { SnapStatus } from '@metamask/snaps-utils';
~~~~~~~~~~~~~~~~~~~~~~~- TODO: Investigate feasibility of using
bundleroption withtsup
Explanation
We need to update --module and --moduleResolution to node16 at minimum and preferably nodenext before we upgrade to TypeScript v5.0, but this entails a number of breaking changes:
Add file extensions to relative import
For example, in an ECMAScript module in Node.js, any relative import needs to include a file extension.
// entry.mjs import * as utils from "./utils"; // ❌ wrong - we need to include the file extension. import * as utils from "./utils.mjs"; // ✅ works
Also investigate allowImportingTsExtensions
Convert to dynamic import
it’s worth noting that the only way to import ESM files from a CJS module is using dynamic import() calls. This can present challenges, but is the behavior in Node.js today.
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html
https://nodejs.org/api/esm.html#esm_interoperability_with_commonjs
Add "type: module" to package.json
That’s why TypeScript 4.7 introduces a new option called moduleDetection. moduleDetection can take on 3 values: "auto" (the default), "legacy" (the same behavior as 4.6 and prior), and "force".
Under the mode "auto", TypeScript will not only look for import and export statements, but it will also check whether
- the "type" field in package.json is set to "module" when running under --module nodenext/--module node16, and
- check whether the current file is a JSX file when running under --jsx react-jsx
In cases where you want every file to be treated as a module, the "force" setting ensures that every non-declaration file is treated as a module. This will be true regardless of how module, moduleResolution, and jsx are configured.
Move type declarations in dependencies to dist/cjs/
src/AccountsController.test.ts:6:28 - error TS7016: Could not find a declaration file for module '@metamask/snaps-utils'. '/home/runner/work/core/core/node_modules/@metamask/snaps-utils/dist/cjs/index.js' implicitly has an 'any' type.
Try `npm i --save-dev @types/metamask__snaps-utils` if it exists or add a new declaration (.d.ts) file containing `declare module '@metamask/snaps-utils';`
6 import { SnapStatus } from '@metamask/snaps-utils';