Flow: Implements interface requires redefinition of implementation types

Created on 24 Apr 2017  路  8Comments  路  Source: facebook/flow

With interface:

import type { $Request, Middleware } from 'express'

type Route = {
    path: string,
    method: string,
    authRequired?: bool,
    admin?: bool,
    middles?:[Middleware],
    handler: (request?:$Request) => Promise<any>
}

export interface Controller {
  get routes(): Array<Route>
}

And implementation:

//@flow
import type Controller from '../framework/Controller'
import type {$Request} from 'express'

class ConfigController implements Controller {
  get routes() {
    return [{
      path:'/config',
      method:'get',
      authRequired:false,
      handler:this.config
    }]
  }
  config(request: $Request) {
    return Promise.resolve({
      autoTweet:false
    })
  }
}
module.exports = ConfigController

get routes()__{
^
return Missing annotation

Most helpful comment

Yeah that's sort of the issue here. As I'm using flow, I'm relying on it to deliver types to me. So my initial intention for implements interface was so that types would be brought in. If the only value of "implements Interface" is to make sure It confirms, then that phrase is slightly useless, because you can get conforms to errors when you try to use class as interface.

As flow devs, we look for ways for types to be delivered to us. We actually don't want to write types all day, we try to massage flow into knowing more, so we have to do less. We need more free types, be it by inference, generation or best of all type operators

All 8 comments

Let me clarify:

If you add a type annotation in the class then everything works right?

The problem also shows up in this example:

export interface IClass {
  test(arg: String): void
}

export class MyClass implements IClass {
  test(arg) { console.log(arg) }
}

Result:

test(arg) {
     ^^^ parameter `arg`. Missing annotation

Note that this error does not get reported when removing the export statements.

I guess this is a non-issue because interfaces are not nominally typed, so there's now way for flow to infer the types of implementers.

This is by design. Flow asks you to annotate along module boundaries to prevent the proliferation of large unions of inferred types, which lead to bad performance downstream.

Interesting, as I thought the whole purpose of an interface was to insure the implementation contract. If MyClass.test's signature overloaded the interface's definition, then I could understand requiring fresh annotation.

Yeah that's sort of the issue here. As I'm using flow, I'm relying on it to deliver types to me. So my initial intention for implements interface was so that types would be brought in. If the only value of "implements Interface" is to make sure It confirms, then that phrase is slightly useless, because you can get conforms to errors when you try to use class as interface.

As flow devs, we look for ways for types to be delivered to us. We actually don't want to write types all day, we try to massage flow into knowing more, so we have to do less. We need more free types, be it by inference, generation or best of all type operators

Flow does require that you write type annotations in many places, which is a crucial part of the design. The way implements is working is also as designed. I'm sorry that those designs don't seem to serve your needs.

@samwgoldman Yeah, but is there any reason flow is designed that way? Like, what is the actual reason I have to define the property types in the class itself again? Why should I even use implements and Interfaces then?

You can say "this is by design" a thousand times but it's just bad design then, if a type checker requires you to write the exact same code twice.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vjpr picture vjpr  路  55Comments

MarcoPolo picture MarcoPolo  路  67Comments

sophiebits picture sophiebits  路  66Comments

jamesisaac picture jamesisaac  路  44Comments

StoneCypher picture StoneCypher  路  253Comments