.js
to .ts
. As TypeScript starts to find errors (and it usually does!), make sure to celebrate your wins—even if they're small!—with your team, especially if some people are not convinced yet. We would also love to hear your stories!strictFunctionTypes
). 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."compilerOptions"
hash (which are also the settings generated by the ember-cli-typescript blueprint):"noEmitOnError": true
(the default) in the "compilerOptions"
hash in your tsconfig.json
. This will fail your build if you have type errors, which gives you the fastest feedback as you add types.any
for them and come back and fill them in later. This will let you do the strictest strictness settings but with an escape hatch that lets you say "We will come back to this when we have more idea how to handle it." This approach lets you move faster, but means you will still have lots of runtime type errors: any
just turns the type-checker off for anything touching those modules. You’ll have to come back later and clean those up, and you’ll likely have more difficult refactorings to do at that time.@types/ember-mocha
or @types/ember-qunit
. Beyond that, look for types from other addons: it will mean writing any
a lot less and getting a lot more help both from your editor and from the compiler.@types
namespace. (In the future we hope to maintain a list of known types; keep your eyes open!)types
directorytypes
directory in the root of your application and add a "paths"
mapping that includes that directory in any type lookups TypeScript tries to do. This is convenient for a few things:vendor
packages which do not have any typestypes/<your app>
directory with an index.d.ts
file in it. Anything which is part of your application but which must be declared globally can go in this file. For example, if you have data attached to the Window
object when the page is loaded (for bootstrapping or whatever other reason), this is a good place to declare it.index.d.ts
file, with the Array
prototype extensions enabled and the Function
prototype extensions commented out. You should configure them to match your own config (which we cannot check during installation). If you are disabling Ember's prototype extensions, you can remove these declarations entirely; we include them because they're enabled in most Ember applications today.config/environment.js
in app/config/environment.d.ts
. This interface will likely require some changes to match your app.config/environment.js
is (a) not actually identical with the types as you inherit them in the content of an application, but rather a superset of what an application has access to, and (b) not in a the same location as the path at which you look it up. The actual config/environment.js
file executes in Node during the build, and Ember CLI writes its result as <my-app>/config/environment
into your build for consumption at runtime.get
or set
this.get
and this.set
will work as you'd expect if you're doing lookups only a single layer deep. Things like this.get('a.b.c')
don't (and can't ever!) type-check; see the blog posts for a more detailed discussion of why.null
s or undefined
s along the way. If nested
is null
at runtime, this will crash!// @ts-ignore
. This will not do any type-checking, but is useful for the cases where you are intentionally checking a path which may be null
or undefined
anywhere long it.inject
functions at runtime, using the name of the service or controller being injected up as the default value—a clever bit of metaprogramming that makes for a nice developer experience. TypeScript cannot do this, because the name of the service or controller to inject isn't available at compile time in the same way.MySession
type annotation this way, but we don't need the string lookup (unless we're giving the service a different name than the usual on the class, as in Ember injections in general). Without the type annotation, the type of session
would just be any
. This is because decorators are not allowed to modify the types of whatever they decorate. As a result, we wouldn't get any type-checking on that session.login
call, and we wouldn't get any auto-completion either. Which would be really sad and take away a lot of the reason we're using TypeScript in the first place!declare
property modifier. This tells TypeScript that the property will be configured by something outside the class (in this case, the decorator), and guarantees it emits spec-compliant JavaScript.ComputedProperty
, because native ES5 getters are not available there, which means that instead of accessing the service via this.mySession
, you would have to access it as this.get('mySession')
or get(this, 'mySession')
.{{user-profile username='example123'}}
, you would expect that username
would have the value of example123
, however prior to the native class feature released in Ember 3.6
, this will result in username
being undefined.3.6
, please use https://github.com/pzuraq/ember-native-class-polyfillfindRecord
, queryRecord
, adapterFor
, serializerFor
, etc. No need to try to write out those (admittedly kind of hairy!) types; just write your Ember Data calls like normal and everything should just work.Service
and Controller
injection. If for some reason you want to opt out of the full type-safe lookup for the strings you pass into methods like findRecord
, adapterFor
, and serializerFor
, you can add these declarations somewhere in your project:Model
, Adapter
, and Serializer
instances instead. It will save you time in even the short run!error TS2344
problemthis
value in several of Ember's test types can include a reference to the Ember Data Store
class.ember-data.d.ts
in your types
directory:Assertion Failed: InjectedProperties should be defined with the inject computed property macros.
– You've written someService = inject()
in an ES6 class body in Ember 3.1+. Replace it with the .extend
approach or by using decorators(@service
or @controller
) as discussed above. Because computed properties of all sorts, including injections, must be set up on a prototype, not on an instance, if you try to use class properties to set up injections, computed properties, the action hash, and so on, you will see this error.Assertion Failed: Attempting to lookup an injected property on an object without a container, ensure that the object was instantiated via a container.
– You failed to pass ...arguments
when you called super
in e.g. a component class constructor
. Always do super(...arguments)
, not just super()
, in your constructor
.node_modules/@types
node_modules/@types
. If the type defs you need are not found there and are not supplied in the root of the package you're referencing, you can register a custom value in paths
in the tsconfig.json
file. See the tsconfig.json docs for details.