Code Monkey home page Code Monkey logo

ember-typings's Introduction

🛑 ARCHIVED 🛑

This repository is now in archival mode, having fulfilled its purpose. We now track type definition issues for the Ember project in the ember-cli-typescript repository.


ember-typings

build status

Development repository for TypeScript type definitions for Ember.js.

The type definitions are perodically published to @types/ember through DefinitelyTyped. You shouldn't depend on this package directly unless you're testing or helping develop new Ember.js ecosystem typings.

Running Tests

yarn test

The tests do not run, the code is only type-checked.

Testing in your project

To use ember-typings instead of @types/ember, first run:

yarn remove @types/ember @types/ember-testing-helpers @types/rsvp
yarn add typed-ember/ember-typings --dev

Then add a paths entry to your tsconfig.json:

{
  "compilerOptions": {
    "paths": {
      "*": [
        "node_modules/ember-typings/types/*"
      ]
    }
  }
  ...
}

ember-typings's People

Contributors

chriskrycho avatar csantero avatar dfreeman avatar dwickern avatar poteto avatar psbanka avatar theroncross avatar vlascik avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

ember-typings's Issues

`this` context lost in computed getters

I played a little with trying to pass around the this context for them, but 😬 that seems like not a lot of fun.

Is our best bet here to lean hard on @computed? Can we bind the this context there more easily, at least in a class method context?

Issues with current Route definitions

Using the latest, I bumped into the following issues:

  • beforeModel and afterModel (a) should be generic and (b) are overly strict. Should probably be something like this:

    beforeModel<T>(transition: Transition): Rsvp.Promise<T>;
    beforeModel(transition: Transition): void;
  • Similarly, model should probably be something like

    model<T>(params: {}, transition: Transition): T | Rsvp.Promise<T>;
    model(params: {}, transition: Transition): void;

I also ran into issues with the ActionsHash bits combined with the user of Route.extend(SomeMixin), but I strongly suspect the mixin pattern is to blame there, rather than the Route definition.

Ember typing issues to fix

I'm in the process of fixing every type error in the ~35k lines of TS in our app, and tracking things that need tweaking in the typings from that or that get flagged up in Slack here.

Ember.js

  • Mixin needs to support multiple arguments to create. Something roughly like this (and out to several):
    static create<T1, T2, Base = Ember.Object>(
      arg1: T1 & ThisType<Fix<T1 & Base>>,
      arg2: T2 & ThisType<Fix<T2 & Base>>
    ): Mixin<T1 & T2, Base>;
  • Mixin needs to have __ember_mixin__ not be private.
  • Route#afterModel and Route#beforeModel should return any since any | Promise<any> just reduces to any.
  • Route#serialize should return object
  • computed property macros
    • or should return ComputedProperty<boolean>
    • and should return ComputedProperty<boolean>

ember-data

  • use RSVP.Promise not native Promise
  • Store#adapterFor needs to be generic over A extends Adapter
  • Store#serializerFor needs to be generic over S extends Serializer
  • Model#serialize's options param should be optional

ember-qunit

  • QUnitModuleCallbacks needs needs?: string[]

TS errors when extending object literal attempts to define/override init

I have a Ember.Service definition below:

import Ember from 'ember';

const { Service } = Ember;

export default Service.extend({
  init() {
    this._super(...arguments);
  }
});

However, TypeScript complains that this is incorrect and throw the exception below:
Error:(6, 3) TS2345:Argument of type '{ init(): void; }' is not assignable to parameter of type 'Mixin'. Object literal may only specify known properties, and 'init' does not exist in type 'Mixin'.

This is supported by the Ember lib and seems like a critical issue

No type checks in other files when exporting `typeof MyModel.prototype` without another `typeof`.

Basically this should throw a typing error:

function makefoo(f : Foo) : string {
  return f.bar;
}

And this is how Foo is defined:

const Foo = Ember.Object.extend({
  name: "Hallo"
});

type Foo = typeof Foo.prototype;
export default Foo;

As workaround this can be done:

function makefoo2(f : typeof Foo) : string {
  return f.bar;
}

This correctly outputs:

app/controllers/application.ts(10,12): error TS2339: Property 'bar' does not exist on type 'Readonly & (new () => { name: string; } & Object) & (new (...args: any[]) => { nam...'

This repo can be used as reproduction.

Ping @dwickern @chriskrycho since we discussed this in slack.

[ember-data]: Type is referenced directly or indirectly in the fulfillment callback of its own 'then' method

When using async/await on an ember Route model, awaiting the result of store.findRecord throws the following typescript exception. (in the comment below)

async model(): Promise<any> {
  const store = get(this, 'store'); 
  await store.findRecord('someStoreRecord', 1); // Type is referenced directly or indirectly in the fulfillment callback of its own 'then' method
  }

Using a .then works as expected. This issue arises with as mentioned, using async/await

Could this be an issue with the return type definition for store.findRecord which is PromiseObject<T> & T?

Wrong typing for `this.addObserver` in a component

Here is a little snippet:

import Component from '@ember/component';

export default class MyComponent extends Component {

	view: string = 'list';

	constructor() {
		super(...arguments);

		this.addObserver('view', () => {
			this.doSomething();
		});
	}
}

Now, typescript will report Expected 3 arguments, but got 2 for the addObserver method. However, this code is absolutely fine. May be that ember itself isn't clear about their method signatures on that case.

Here is the code trace to this:

  1. Component extends CoreView extends CoreObject which includes the mixin Observable
  2. Observable.addObserver is defined here https://github.com/emberjs/ember.js/blob/3a4b38000b2cc3ce49d93f4df45415db545b3752/packages/ember-runtime/lib/mixins/observable.js#L353 and passes it down to addObserver from ember-metal
  3. Here is addObserver at ember-metal https://github.com/emberjs/ember.js/blob/3a4b38000b2cc3ce49d93f4df45415db545b3752/packages/ember-metal/lib/observer.js#L23 which passes it down to addListener
  4. Here is addListener with the code line that says, the third argument is optional: https://github.com/emberjs/ember.js/blob/3a4b38000b2cc3ce49d93f4df45415db545b3752/packages/ember-metal/lib/events.js#L60

Here is the relevant typing to that (if I'm correct): https://github.com/DefinitelyTyped/DefinitelyTyped/blob/e22846ad77459b3ece25598db93c2013e8c76716/types/ember/index.d.ts#L1683

I would have fixed this on my own, but these typings look like perl to me (😂).

[DRAFT!!!] QUEST: Let's type the Ember.js ecosystem!

*DRAFT – STILL WORKING OUT THE DETAILS HERE! DO NOT JUMP IN JUST YET!

Let’s type the Ember.js ecosystem!

Overview

The goal of this quest issue is to write TypeScript type definitions to cover the top 100 addons listed on Ember Observer as of the time the quest is launched. Our bonus goal is to type any other addons which want to participate, as well!

Philosophy

There are two overarching concerns that drive everything else in this quest:

  1. Good type definitions are worth their weight in gold. Bad type definitions are worse than no type definitions at all. Accordingly, our goal is not just to get some type definitions written, but to write good type definitions that actually give us these benefits, and which don’t mislead us or give us a false sense of security.

  2. We should be good citizens of the Ember.js community. We want to use TypeScript to be helpful; we do not want to be pushy jerks or typed-language fanatics. As part of the Ember.js ecosystem, we’re all in this together, and those of us working on the TypeScript side should never look down on people who prefer JavaScript – there are legitimate tradeoffs either direction. So, as always, let’s be kind!

Improving developer experience for all Ember.js users

By doing this, we can make the experience of developing Ember apps better for everyone – not just TypeScript users, but all the people using plain-old JavaScript as well. An increasing number of editors will take advantage of TypeScript type definitions if they’re available to help you in your development experience, even if you’re not using TypeScript at all. And of course, if you are using TypeScript, having these will make all the difference in your experience of using an addon. So let’s do it!

How to participate

First, a couple notes to both add-on authors and add-on users, then the nitty gritty!

Addon authors

{>> TODO <<}

If you are open to someone else adding typings for your addon

One other approach you can take, if you're interested in adding types to your addon but don't have the knowledge or bandwidth to do it yourself is to add a Help Wanted issue to your repository and link it here! If your repo isn't in the top-100-addons list, I'll add it anyway in its own section. If you're

{>> TODO <<}

Add-on users

If you take a look at one of the addons on this list and the author hasn’t yet expressed any interest in adding TypeScript or TypeScript definitions to their addon, the very first thing you should do is open an issue and see if they’re open to the possibility of converting their addon to TypeScript or hosting the type definitions in the addon. We strongly recommend you just use this template when opening the issue:

  • Suggested Title: Interested in TypeScript?

  • Suggested Body:

    Hello! I use your addon and really appreciate it. I’m also participating in this quest issue to add TypeScript support throughout the Ember.js ecosystem. This will benefit to everyone who uses your addon, not just TypeScript users! (See here for an explanation.)

    Are you interested in either converting the addon to use TypeScript itself, or in adding type definitions? I’d love to help out, if so! And if not, that’s just fine. Thanks!

    To make it easy, you can just copy and paste this directly into the issue of a body:

    Hello! I use your addon and really appreciate it. I’m also participating in [this quest issue to add TypeScript support throughout the Ember.js ecosystem](https://github.com/typed-ember/ember-typings/issues/14). This will benefit to *everyone* who uses your addon, not just TypeScript users! (See [here]( improving-developer-experience-for-all-emberjs-users) for an explanation.)
    
    Are you interested in either converting the addon to use TypeScript itself, or in adding type definitions? I’d love to help out, if so! And if not, that’s just fine. Thanks!
    

Based on their answer, you can then help them convert their addon to use TypeScript, help them add type definitions to the repository, or – and this is very important – just leave them alone if they’re not interested! It’s perfectly fine if addon authors aren’t interested in using TypeScript themselves or if they don’t want to take on the extra maintenance burden of hosting type definitions. Do not badger an addon author about TypeScript! If they aren’t interested, we can fall back to using DefinitelyTyped to host type definitions!

Actually doing the work

Okay, let's dive in and talk about how to do this!

Converting an addon to TypeScript

{>> TODO <<}

Writing type definitions

As noted above, the most important thing about type definitions is that they are correct. If they're incorrect, they'll actually lead users to write wrong code, and that's a deeply frustrating experience.

Some general tips:

  1. Wherever there is documentation, read it very carefully. It's not always correct or up to date, but it still usually gives you a good starting point. If nothing else, it is often a good guide for what the public API is.

  2. Read the source code for the public API. This is the only way to actually be sure of what the code does. Take a look at the function arguments, and look at what is done with them. Are there optional arguments? Are the arguments "polymorphic," i.e. can you pass in either a string or an array of strings or an object as the nth argument? Does it always return the same thing or not? Etc.

  3. Pick something you can handle! Your type definitions should capture everything about the actual behavior of an addon's classes and functions. Sometimes, this will be easy. Other times (here's looking at you, Ember CLI Mirage!) this will involve a lot of arcane type mechanics. If you're just starting out, that's okay! Pick a simpler addon. If you've got mad TypeScript chops, awesome: pick something harder!

  4. Don't go it alone. Get input along the way. The folks on the ember-cli-typescript team and many of the people hanging out in #topic-typescript on the Ember Community Slack are happy to help review your typings work and help you go from just wrote my first type definition ever! to mentoring other people! And getting feedback along the way will help us achieve our goal

{>> TODO: how to write a good type definition <<}

Type definition basics

{>> TODO: .d.ts files, modules, exports <<}

Writing good type definitions

For more complicated types, you may need to lean on TypeScript's optionals, generics, union and intersection types, overloading, or the combination of several of those.

Optionals

One of the most common scenarios you'll need to cover is handling optional arguments or properties

Arguments

{>> TODO <<}

Properties

There are two ways you'll commonly need to write out optional properties. One is when some items on a type are required and others are optional. In that case, you can write the type with each argument specified explicitly as being optional or not:

type ATypicalWesternName = {
  first: string;
  middle?: string;
  last: string;
}

The other case is where every property on an object is optional. (This tends to come up when you're passing in a configuration/options hash to a function.) In that case, you can use the build-in Partial<T> type, which makes every property optional:

type Options = {
  doSomething: boolean;
  withSomeValue: number;
};

export declare function takesOptions(options: Partial<Options>);

If any of the arguments are required, don't use Partial, spell it out explicitly. Alternatively, build an intersection type from the combination of required properties and optional properties.

type WesternRequiredName = {
  first: string;
  last: string;
}

type WesternOptionalName = {
  middle?: string;
}

type WesternName = WesternRequiredName & WesternOptionalName;

(This is something of a last resort, but it's there when you need it!)

Union types

Union types (described here) let you describe the either-or scenario. If a function can take either a number or a string or a boolean as an argument, you can write it like this:

declare function takesAUnion(firstArg: number | string | boolean);

Likewise, if a function returns more than one kind of things, you can write it like this:

declare function returnsAUnion(): number | string | boolean;

This is most useful for times when there is no distinct mapping between the input and output type. For example, the helpers in the ember-native-dom-helpers helpers (now living at @ember/test-helpers) all take either a string or an HTMLElement as their argument, but they always return the same kind of thing. The function signature for click, for example, is:

export function click(selector: string | Element): Promise<void>;

However, in some circumstances, a function might take more than one input type and have different output types – but each kind of input would always have the same kind of output. For that, we'll use overloading.

Overloading

With overloading, we can tell TypeScript that a single function or method has different "signatures." For example, you may recall that {>> TODO: example <<}

Generics

Structuring type definitions

The basic structure of the type definition should mirror the structure of the addon or library you're documenting. However, the layout of Ember addons imposes specific requirements that are somewhat unique. In general in TypeScript, it is a good idea to lay out more complicated definitions side-by-side with the modules they document:

src/
  index.js
  index.d.ts
  some-other-module.js
  some-other-module.d.ts

However, in the current file system layout for Ember addons, the source location and the "lookup location" where you import the files from are not the same. That is, you don't import Foo from 'an-addon/addon/services/foo';, you import Foo from 'an-addon/services/foo';. This essentially constrains us to writing all of our type definitions in a single file. (The Module Unification file layout will help with this, but it will be some time before most addons are using it.)

Aside: The Ember types, as they stand today, are not a good example for how to document most libraries: we have documented Ember as it exists, where you can import things both in the new modules API and via the old global. As Ember itself moves to using the module structure under the hood and those packages are actually installed on the file system, type definitions should move to mirror them.

For a simple addon you can just put the type definitions in the root at index.d.ts, like this:

the-addon/
  addon/
    index.js
  app/
  index.d.ts
  index.js
  package.json

A prime example of this kind of setup is ember-test-friendly-error-handler.

{>> TODO: example addon for multiple modules in index.d.ts – ember-qunit maybe? <<}

{>> TODO: structure – modules etc. <<}

Adding type definitions to an addon

[Reminder: only do this after you've checked with the author(s) of the addon that they're interested!]

Once you have a version of the types working solidly in your own application(s), you can open a pull request to the addon with those types. The pull request should include:

  • the index.d.ts file in the root of the repository
  • the "types" key in package.json pointing to the file – for the sake of forwards-compatibility, so that if the type definitions are expanded-and-moved at a later time, things don't surprisingly break
  • a note in the README that type definitions are supplied with the addon

It should also have a good writeup, describing any open questions, pain points, limitations, etc. – that will help the addon maintainer and the reviewers help you wrap everything up with a bow!

Your best bet is to solicit feedback from both the addon maintainer and from knowledgeable folks in #topic-typescript on the Ember Community Slack. The Typed Ember team, currently consisting of @dfreeman, @dwickern, @jamescdavis, and @chriskrycho, are happy to help review these requests, and over time others will be experienced enough to chip in as well!

If you get feedback that things need tweaking, don't worry! Type definitions are hard to get just right, and the definitions for Ember have been through a ton of revisions and we're constantly finding issues with and helping each other with those, too!

Publication

You should also make sure the type definition is not excluded by .npmignore file and if the package.json is manually specifying files via the "files" key, that index.d.ts is in the list. (This is unusual for Ember addons.) See the documentation for the files key for details.

Adding type definitions to DefinitelyTyped

{>> TODO <<}

DefinitelyTyped is an officially supported, Microsoft-owned GitHub repository which publishes to the @types npm namespace. Type definitions are automatically published from each folder in the types directory in the repository.

The process when using DefinitelyTyped is mostly the same as when adding them directly to an addon. When opening a PR for a set of types on DefinitelyTyped, as when doing it directly to an addon, you should get reviews from other folks who are comfortable with both TypeScript and the addon. The Typed Ember team, currently consisting of @dfreeman, @dwickern, @jamescdavis, and @chriskrycho, are happy to help review these requests, and over time others will be experienced enough to chip in as well!

Once it's in place, if the addon author is up for it, you can then open a PR to add a note to their README indicating that types are available at @types/<the-addon-name>. You should also open a PR to this repository to update the known-typings.md file.

Typings to contribute

I’ve broken this down into two categories: Top 100 Addons on Ember Observer and Other addons. Both of these are important, albeit for different reasons.

The top 100 addons on Ember Observer represent the most-installed addons in the community; their importance is probably obvious—getting typings in place for them immediately impacts the most people. Other addons or packages are those which may be downloaded less, but whose authors actively want to participate in the quest! We want to support both of these.

Top 100 Addons on Ember Observer

The top 100 add-ons in the ecosystem, by way of Ember Observer! Some of these may not need typings added; we’ll remove them as we evaluate that. There is also quite a range of variation in the complexity of these.

Other addons

If your addon isn't in the top 100 on Ember Observer but you volunteer it, we'll list it here!

Interface 'NativeArray<T>' incorrectly extends interface 'MutableArray<T>'

A brand new ember-cli 2.18 application with latest ember-cli-typescript installed, ember serve will shows warning above:

WARNING: node_modules/@types/ember/index.d.ts(1614,19): error TS2430: Interface 'NativeArray<T>' incorrectly extends interface 'MutableArray<T>'.
  Types of property 'filter' are incompatible.
    Type '{ <S extends T>(callbackfn: (value: T, index: number, array: T[]) => value is S, thisArg?: any): ...' is not assignable to type '{ <S extends T>(callbackfn: (value: T, index: number, array: T[]) => value is S, thisArg?: any): ...'. Two different types with this name exist, but they are unrelated.
      Type 'any[]' is not assignable to type 'NativeArray<T>'.
        Property 'replace' is missing in type 'any[]'.

These're all types installed:

  ▾ @types/
    ▸ ember/
    ▸ ember-testing-helpers/
    ▸ handlebars/
    ▸ jquery/
    ▸ rsvp/

Typings for ember prototype extensions

Ember modifies the prototype for String, Function and Array based on EmberENV.EXTEND_PROTOTYPES

// function prototype is used for computed properties
function() {
  return this.get('name')
}.property('name')

// string prototype is used for random stuff
'test'.capitalize()

// array prototype is used for various functionality
['test'].get('firstObject')
arr.findBy('id', 5)

I'd like to start a discussion about which typings we should provide for these

WARNINGs when using 'get()'

Hi there. I am trying to get ember-cli-typescript up and running but I'm encountering some rather fundamental issues. Something about the type declarations of get() is funny.

I am encountering a few issues. The first of which is in a class-based pattern (which I'd like to use, ideally):

import Controller from '@ember/controller';
import { get, computed } from '@ember/object';

export default class ApplicationController extends Controller {
  value: string;
  
  init() {
    console.log(get(this, 'value'));
  }
}

I get a warning on the line get(this, 'value'), specifically on the token this:

WARNING: ember-typescript/controllers/application.ts(8,21): error TS2345: Argument of type 'this' is not assignable to parameter of type 'ComputedProperties<{ value: string; init: () => void; replaceRoute: (name: string, ...args: any[]...'.

It can't seem to assign type ApplicationController to ComputedProperties<T>. I can't quite grasp the intent with this type.

The second use-case is when using a more standard Controller.extend pattern:

import Controller from '@ember/controller';
import { get, computed } from '@ember/object';

export default Controller.extend({
  value: 2,

  debug: computed('value', function() {
    let value = get(this, 'value');
  }),
});

I get a warning warning on the line get(this, 'value'), again, specifically on the token this:

WARNING: ember-typescript/controllers/application.ts(8,27): error TS2345: Argument of type '"value"' is not assignable to parameter of type 'never'.

This seems to be because this is of type any as defined here:

    type ComputedPropertyGetterFunction<T> = (this: any, key: string) => T;

in @types/ember/index.d.ts line 76.

It's worth mentioning that this app completely functions in either scenario but it seems kind of misconfigured somehow as this is a completely vanilla app trying to do something completely standard. Any ideas what the issue is here?

Mark as deprecated?

From what I can tell, this repo is no longer active, and changes to @types/ember are made directly in the DefinitelyTyped project.

To prevent newcomers from opening issues ( #77 ) here and thinking they'll get attention, we should indicate that this repo is no longer in use

Using ES classes with Route, model, and async

Hey everyone!

I ran into an issue when using ES classes and trying to use async/await on the Route's model hook. Use case is below:

class MyRoute extends Route {
  async model(): Promise<any> {
    const prop = await this.get('store').findRecord('someRecord', 'someParam');
    return { prop };  
  }
}

However, the above would through the following error

Class 'MyRoute' incorrectly extends base class 'Route'.
  Types of property 'model' are incompatible.
    Type '() => Promise<any>' is not assignable to type '<T>(params: {}, transition: Transition) => T | Promise<T>'.
      Type 'Promise<any>' is not assignable to type 'T | Promise<T>'.
        Type 'Promise<any>' is not assignable to type 'Promise<T>'.
          Property 'new' is missing in type 'Promise<any>'.

I'm assuming because the definition of model() in Route is expected to return T | Promise<T> However, if you try to change the return type for the above to

async model(): any | Promise<any>

Then you lose the above error for Route and get one for async instead:

[ts] The return type of an async function or method must be the global Promise<T> type.

Was wondering if this is an issue that should be fixed or if there is some insight that I'm missing. Thanks!

Missing re-export for Rsvp

tsc throws an exception for references to Ember.RSVP.Promise:"TS2694: Namespace 'Ember' has no exported member 'RSVP'". This worked in a previous version of @types/ember.
An example of a typing that fails is:
(urn: string): Ember.RSVP.Promise<IMetric, any>

Thanks

Typings for qunit

These modules are important since they're included by the default ember-cli blueprint.

import { test, module } from 'qunit';
import { setResolver } from 'ember-qunit';
import { start } from 'ember-cli-qunit';

qunit typings are published from DT (@types/qunit). It exports a global QUnit and that's it. ember-qunit adds the qunit exports (source) in addition to its own exports.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.