5

I am trying to write a type definition for flummox but I don't fully understand how I am supposed to write it. The gist is that we are supposed to subclass Store, Actions and Flummox classes and pass them to functions.
Here's a code example with the actions:

import { Flummox, Actions, Store } from 'flummox';
class MessageActions extends Actions {
  newMessage(content: string) {
    return content;
  }
 }
class MessageStore extends Store {
   constructor(flux: Flummox) {
   super();

   const messageActions: MessageActions = flux.getActions('messages'); // error here
   this.register(messageActions.newMessage, this.handleNewMessage);
  }
 }

 class Flux extends Flummox {
   constructor() {
     super();

     this.createActions('messages', MessageActions);
     this.createStore('messages', MessageStore, this); //error here
   }
 }

And the definition I started:

/// <reference path="eventemitter3.d.ts"/>

declare module "flummox" {

  type ActionId = string;

  class Store {
    register(action: ActionId | Function, handler: Function): void;
  }

  class Actions {

  }

  class Flummox extends EventEmitter3.EventEmitter.EventEmitter3 {
    createActions(key: string, actions: Actions, constructorArgs?: any | any[]): Actions;
    getActions(key: string): Actions;
    removeActions(key: string): Actions;

    createStore(key: string, store: Store, constructorArgs?: any | any[]): Store;
    getStore(key: string): Store;
    removeStore(key: string): Store;
 }
}

I am getting the following error:

src/app/App.ts(16,11): error TS2322: Type 'Actions' is not assignable to type 'MessageActions'. Property 'newMessage' is missing in type 'Actions'. src/app/App.ts(35,34): error TS2345: Argument of type 'typeof MessageStore' is not assignable to parameter of type 'Store'. Property 'register' is missing in type 'typeof MessageStore'.'.

which is fair enough since I know I should probably use interface but then I won't be able to extend the classes in my code. Here's a link to the repo in case you want to try it

Would anyone be able to help me? I feel like I'm missing something obvious

2
  • which line has the error? Commented May 28, 2015 at 10:38
  • Updated the post with the complete App.ts and a link to the repo, sorry about that Commented May 28, 2015 at 10:57

2 Answers 2

5

As discussed on IRC, createStore(store: Store) means createStore takes an instance of type Store (or its subtypes), not a type that's a subtype of Store. For the latter, you want store to be of a type that contains a construct signature that returns Store or a subtype of Store. So

createActions(key: string, actions: Actions, constructorArgs?: any | any[]): Actions;

should be either

createActions(key: string, actions: { new(...args: any[]): Actions }, constructorArgs?: any | any[]): Actions;

or

createActions<T extends Actions>(key: string, actions: { new(...args: any[]): T }, constructorArgs?: any | any[]): T;

The latter allows you to return the same type you passed in instead of always returning Actions, should you need it.

The same needs to be done for createStore()


Also

this.register(messageActions.newMessage, this.handleNewMessage);

will cause problems because this will be undefined in handleNewMessage. this.handleNewMessage returns a function, not a bound delegate like in other languages. You either want this.handleNewMessage.bind(this) or message => this.handleNewMessage(message) - the latter form requires you to explicitly write out all the parameters, whereas the former form doesn't but loses typechecking errors if the signatures of the parameter to register and of handleNewMessage ever disagree.

Sign up to request clarification or add additional context in comments.

Comments

0

Typescript definition for subclass as parameter

This is not the error here. The code you have posted is fine:

declare class Actions {
}

declare class Flummox {
  createActions(key: string, store: Actions): Actions;
}

class MessageActions extends Actions {
  newMessage(content: string) {
    return content;
  }
}

class Flux extends Flummox {
  constructor() {
    super();

    this.createActions('messages', MessageActions);
  }
}

Try it

You are allowed to pass subclasses as a parameter if a parent class is expected.

2 Comments

Actually their problem is that createStore(store: Store) means createStore takes an instance of type Store, whereas they want to pass in a type derived from Store. So createActions / createStore need to take in types with construct signatures that return Action / Store instead of Action / Store themselves.
(Your playground happens to work only because your Actions type is empty, so the MessageActions type is okay to be an instance of the Actions type.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.