This repository is no longer being mainained.
Please refer to the Ember&TypeScript repositories at typed-ember
:
Also be sure to checkout #topic-typescript on the Ember slack community!
This README outlines the details of collaborating on this Ember addon (see: "Contributing")
For more information on using ember-cli, visit https://ember-cli.com/.
For more information about TypeScript, visit www.typescriptlang.org/.
Please note that the type declarations are in an early stage and far from complete. The addon itself hasn't been fully tested and won't catch all errors in your code (although it will help a lot!). Also the way the Broccoli funnel set registers the TypeScript preprocessor is very hacky (see index.js
: if you have any other .js preprocessors besides Babel, this hack might break your ember build.
If you want to help out with this project, pull requests are welcomed!
In short, ember-cli-typescript depends @types/ember, which definitions are based on TypeScript 1.x. While providing autocompletion features, these definitions provide little type safety.
ember-typescript2 includes rewritten TypeScript 2.3 definitions (still a work in progress), and
an experimental application.d.ts
file that enables type checking on models and services too.
- fixed
ThisType<T>
annotation for Ember.Object.extend (now all your methods should have the rightthis
context!) - fixed
this.store.createRecord
type - added basic types for BuildURLMixin
- silenced warnings for internal Ember CLI modules when running
ember serve
A rewrite of Ember 2.x types and interfaces, based on Typescript 2.3+ features such as:
- indexed signatures
- ThisType
- keyof and lookup types
- This addon runs Typescript during the Ember CLI build process (using
broccoli-typescript-compiler
) - All
.ts
and.js
scripts in the app/ folder are type checked and compiled viaember serve
- Any type errors in your app are displayed in the Ember CLI console
- typescript@2.3.4
- broccoli-typescript-compiler
To install this addon and start using Typescript in your Ember project:
$ cd my-ember-app/
$ ember install ember-typescript2
This will install a tsconfig.json, and some extendable interfaces in app/types/application.d.ts (see section ModelTypeIndex below).
Run ember serve and you should see the result of Typescript type checking:
$ cd my-ember-app/
$ ember serve
If you are using Visual Studio Code, you can enable its Typescript Language mode while editing .js files:
Menu: View > Command Pallette .... > Language Mode > Typescript
Note that for your convenience, a tsconfig.json is installed in your projects root. This allows for better IDE integration, and you can also run Typescript in standalone mode on your project:
Install Typescript globally:
$ npm install -g typescript
Now run tsc in your project, and get pretty printed type errors:
$ cd my-ember-app/
$ tsc
To get Typechecking working for your models and services, have a look at app/types/application.d.ts (automatically generated by ember-typescript2)
For more information, see the section Advanced configuration below.
For some Ember application code, the bundled Typescript 2.3 definitions work out of the box. This means you will now get type checking for things like:
This addon was created in order to experiment with TypeScript inside of Ember applications.
The goal is to have a simple drop-in addon which enables typechecking across any Ember app, without a need to rewrite any code.
While some minor configuration of the app/types/application.d.ts file is recommended (see section Configuration), this means you can keep writing your app in the conventional Ember CLI style, and get TypeScript type checking out of the box:
import Ember from 'ember';
export default Ember.Route.extend({
setupController(controller, model) {
var user = this.store.createRecord('user');
user.set('nonExistent', true);
^^^^^^^^^^^
type error because property does not exist on type User
}
});
Please note that TypeScript needs to know about your Models and Services. Please refer to the section ModelTypeIndex and ServiceTypeIndex
As of Typescript 2.3, it is now possible to properly set the this
context within Ember methods. Where the following code would previously break down, Typescript can now understand the following Ember code:
export default Ember.Component.extend({
color: 'red',
logColor() {
let color = this.get('color');
// ^-- the type of `color` is "string"
console.log(`Color: ${color}`);
}
});
TypeScript 2.1 comes with keyof
and lookup types
, this enables TypeScript to statically check if an object has certain properties, and also retrieve the type of those properties:
let MyObj = Ember.Object.create({
color: "red"
})
var color = MyObj.get('color');
// ^-- color is of type "string"
var hasShadow = MyObj.get('hasShadow');
// ^^^^^^^^^^
// Type error: `hasShadow` is not a property of `MyObj`
object.get(key)
- existence of
key
onobject
is checked - the returned value has its type information preserved
object.set(key, val)
- existence of
key
onobject
is checked - the type of
val
is checked against the type ofobject.key
TypeScript's type inference features makes it possible to generate types for your Ember models and Services:
this.store.createRecord('user', /* ... */)
- the existence of a model with name
user
is checked - the created record will be of type User (NOTE: see section MoodelTypeIndex below to set this up)
- model.get(key) works (as model extends from Ember.Object)
WARNING: computed properties aren't TypeSafe, and you won't get a Type error if you try to call set() on a computed property. However if you define your computed properties as readOnly(), Ember will give you a runtime error, which is still preferable above a silent failure
- type errors when using deprecated Ember feature flags like MODEL_FACTORY_INJECTIONS (needs to be extended)
- Ember.String types (work in progress)
- Ember.Route hooks such as
model
andsetupController
are provided with access tothis.store
- Possibility to define interfaces in your model and service files
The proper this
context is not available inside computed property definitions yet:
export default Ember.Object.extend({
observedProp: "hello",
computedProp: Ember.computed('observedProp', function() {
let value = this.get('observedProp');
// ^-- type of `this` is unknown (any)
// so typechecking is disabled here
})
}
let MyObj = Ember.Object.create({
observedProp: "hello",
computedProp: Ember.computed('observedProp', function() {
let value : string = this.get('observedProp');
// type of 'value' is declared to be string
return value;
})
}
let observedVal = MyObj.get('observedProp')
// ^--- type of `observedVal` is string
let computedVal = MyObj.get('computedProp')
// ^--- type of `computedVal` is string
MyObj.set('computedProp', 'a new value'):
// no warnings from typescript when overwriting a computed property
When writing code that uses MyObj
, it will seem like computedProp
is a simple string, while it is actually a computed property.
Ideally we would be able to distinquish between readonly computed properties and simple properties of an object.
To workaround this problem on inferred model types, you could define your own explicit interfaces for your models, and declare some properties to be
readonly
.
A little bit of configuration is needed to enable typechecking on models and services.
This section describes how to make use of the generated app/types/application.d.ts
.
EmberJS application code sometimes requires you to refer to your models, controllers or services by name (which is just a simple string
). For example
var user = this.store.createRecord('user', /* ... */);
// what is the type of `user`?
Ember.service.inject('my-logger-service');
// where to find 'my-logger-service'?
Typescript can't know that the string "user"
maps to a User
model. In order to link a models name to its type, we needed to provide Typescript with some sort of index:
// pseudocode - please refer to the actual model index in app/types/application.d.ts
import User from "../models/user"
interface ModelTypeIndex {
user: typeof User
}
Using the above ModelTypeIndex
, Typescript is able to understand
var user = this.store.createRecord('user', /* ... */);
// user is of type Model
user.set('nonExistentKey', true);
// ^^^^^^^^^^^^^^ this is a type error (key does not exist on user)
user.set('firstName', 42);
// ^^ this is a type error (int is not a string)
Service names are mapped to their respective types via the ServiceTypeIndex
Please refer to ModelTypeIndex
for how this works, and the app/types/application.d.ts file.
If you come accross any issues, we would really appricate it if you report it in our Github Issue tracker.
Pull requests are welcome! This addon still needs a lot of work, and we are very happy to get input from the Ember and Typescript communities.