-
-
Notifications
You must be signed in to change notification settings - Fork 252
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
const
Type Parameters:- Causes
as const
inference by default. - 74
as const
instances 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
enum
s are unionenum
s enum
overhaul- 37
export enum
instances in core.
- All
- Decorators
Blockers
node16
/nodenext
-
TypeScript v4.7 introduced the
node16
/nodenext
options for the--module
and--moduleResolution
compiler options. -
We are currently at v4.8 using the
node
option formoduleResolution
, which is renamed tonode10
starting from TypeScript v5.0 (thenode
alias is maintained for compatibility).node10
does not support correct module resolution for ESM packages.node10
does not support theexports
field in package.json
bundler
- v5.0 introduces the
bundler
option 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.
bundler
requires--module
to 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
bundler
option 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';