These directions are for converting an existing Ember app to TypeScript. If you are starting a new app, you can use the directions in Getting Started.
Enable TypeScript Features
Install TypeScript and Related Packages
See Getting Started: Packages to Support TypeScript for descriptions of these packages.
npm add --save-dev typescript @tsconfig/ember
npm add --save-dev @types/qunit @types/rsvp
npm add --save-dev @typescript-eslint/eslint-plugin @typescript-eslint/parser
npm remove @babel/plugin-proposal-decorators @babel/eslint-parser
Add TypeScript Configuration
Add a tsconfig.json
file to the root of your project. Copy its contents from the current output from the Ember CLI blueprints.
Set Up TypeScript for EmberData
Follow the instructions in the EmberData Typescript Guides.
Enable TypeScript Transpilation for Builds
To enable TypeScript transpilation in your app, simply add the corresponding configuration for Babel to your ember-cli-build.js
file.
module.exports = function (defaults) {
const app = new EmberApp(defaults, {
"ember-cli-babel": { enableTypeScriptTransform: true },
// ...
});
return app.toTree();
};
Enable Type Checking in CI
To easily check types with the command line, add the lint:types
script as shown here.
The default lint
script generated by Ember CLI will include the lint:types
script automatically.
Configure Blueprint Generators to Use TypeScript
With the following configuration, project files will be generated with .ts
extensions instead of .js
:
{
"isTypeScriptProject": false,
"isTypeScriptProject": true,
}
{
// ...
"packages": [
{
"name": "ember-cli",
// ...
"blueprints": [
{
// ...
"options": [
// ...
"--typescript"
]
}
]
}
]
}
Configure ESLint
Then, update your eslint.config.mjs
to include the current output from the Ember CLI blueprints. You might consider using ESLint overrides configuration to separately configure your JavaScript and TypeScript files during the migration.
Add Initial Type Declarations
Add types for your config/environment.js
file by creating a type declaration file at app/config/environment.d.ts
. You can find an example file in the current output from the Ember CLI blueprints.
Migrate Existing Code to TypeScript
Once you have set up TypeScript following the guides above, you can begin to migrate your files incrementally by changing their extensions from .js
to .ts
. Fortunately, TypeScript allows for gradual typing. This means that you can use TypeScript and JavaScript files interchangeably, so you can convert your app piecemeal.
Some specific tips for success on the technical front:
Strictness
Use the strictest strictness settings that our typings allow. While it may be tempting to start with the loosest strictness settings and then to tighten them down as you go, this will actually mean that "getting your app type-checking" will become a repeated process—getting it type-checking with every new strictness setting you enable—rather than something you do just once.
Gradual Typing Hacks
Many of your files might reference types in other files that haven't been converted yet. There are several strategies you can employ to avoid a chain-reaction resulting in having to convert your entire app at once:
The unknown
type—You can sometimes get pretty far just by annotating types as unknown
. If unknown
is too wide of a type, however, you'll need a more robust solution.
TypeScript declaration files (.d.ts
)—These files are a straightforward way to document TypeScript types for JavaScript files without converting them. One downside of declaration files, however, is that they can easily get out-of-sync with the corresponding JavaScript file, so we only recommend this option as a temporary step.
JSDoc and allowJs
—Another way to document TypeScript types for JavaScript files without converting them is to add JSDoc "type hints" to the files and enable the allowJs
compiler option in your tsconfig.json
. While the JSDoc type syntax can be a bit cumbersome, it is much more likely to stay in sync. You can even type-check your JavaScript files using the @ts-check
directive.
The any
type—Opt out of type checking altogether for a value by annotating it as any
.
The @ts-expect-error
directive—A better strategy than any
, however, is to mark offending parts of your code with a @ts-expect-error
directive. This comment will ignore a type-checking error and allow the TypeScript compiler to assume that the value is of the type any
. If the code stops triggering the error, TypeScript will let you know.
Outer Leaves First
A good approach to gradual typing is to start at your outer "leaf" modules (the ones that don't import anything else from your app, only from Ember or third-party libraries) and then work your way "inward" (toward the modules with many internal imports). Often the highest-value modules are your EmberData models and any core services that are used everywhere else in the app–and those are also the ones that tend to have the most cascading effects (having to update tons of other places in your app) when you type them later in the process. By starting with the outer leaves, you won't have to use as many of our gradual typing hacks.
Prefer Octane Idioms
In general, we recommend migrating to Octane idioms before, or in conjunction with, your migration to TypeScript. See "Working With Ember Classic" for more details.
ember-cli-typescript
The ember-cli-typescript
package was used to add TypeScript support to Ember apps before Ember's native TypeScript support was available. You do not need ember-cli-typescript
installed for new apps or addons.
If you're migrating from ember-cli-typescript
to Ember's native TypeScript support, most of your existing configuration will still be relevant. Just read through the steps of this guide and ensure that your config matches the expected config as described above.