Search code examples
typescriptember.jsember-data

How to fix type checking when using this.store.findRecod


In my ember.js project, I type check javascript files with Typescript and the checkJs option.

Here is a simple route.js file example

import Route from '@ember/routing/route';

export default class UsersRoute extends Route {
  model() {
    return this.store.findAll('user');
  }

}

With this code, I get the following Typescript error

route.js:5:31 - error TS2345: Argument of type '"user"' is not assignable to parameter of type 'never'.

5     return this.store.findAll('user');
                                ~~~~~~

The ember-data type definitions are provided by the @types/ember-data package, and here is the findAll definition

findAll<K extends keyof ModelRegistry>(
    modelName: K,
    options?: {
        reload?: boolean;
        backgroundReload?: boolean;
        include?: string;
        adapterOptions?: any;
    }
): PromiseArray<ModelRegistry[K]>;

This is how far I could go while investigating how findAll was defined.

In theory, this.store.findAll('user'); is valid, but what should I do to fix this Typescript error?


Solution

  • To understand what's happening I recommend reading this section of the ember-cli-typescript docs. The gist is that the types supplied for Ember do some extra work to make it so that when you type this.findRecord('person', 1), the thing you get back is a Person (e.g. the DS.Model you've defined). To make that work, the types define a "registry"—a mapping between the string name and the type to return. That "registry" being empty is the problem you're seeing in the types.

    The default configuration with ember-cli-typescript makes this "just work", by setting up the paths key in tsconfig.json to include "*": "types/*" and including this file at <your app directory>/types/ember-data/types/registries/model.d.ts:

    /**
     * Catch-all for ember-data.
     */
    export default interface ModelRegistry {
      [key: string]: any;
    }
    

    That makes all string lookups for models "work" by just making the type be any.

    If you want to actually define types, you'll need to create a user.d.ts file located right next to the user.js which exports the model type and which includes this:

    declare module 'ember-data/types/registries/model' {
      export default interface ModelRegistry {
        'user': User;
      }
    }
    

    Running ember generate ember-cli-typescript if you're already using it will update these things for you. You can also manually create that file with those contents and set up the paths mapping (and you may want to add the other paths mappings from the default blueprint as well).