user578895
user578895

Reputation:

flow string is incompatible with string literal?

I'm trying to annotate a custom event emitter and restrict the event names to specific strings. For this example though I'll just use one event name:

class MyEmitter extends EventEmitter {
  on(event: 'foo', listener: Function): this {
    return super.on(event, listener);
  }
}

Flow doesn't like this though and complains that 'foo' is not compatible with string??

 24: class MyEmitter extends EventEmitter {
           ^^^^^^^^^^^^ MyEmitter. Cannot extend
 24: class MyEmitter extends EventEmitter {
                             ^^^^^^^^^^^^ EventEmitter
  Property `on` is incompatible:
           v-------------------------------------------
     25:   on(event: 'foo', listener: Function): this {
     26:     return super.on(event, listener);
     27:   }
           ^ function. This type is incompatible with
    660:   on(event: string, listener: Function): this;
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function type. See lib: /private/tmp/flow/flowlib_15c48d81/node.js:660
      The first parameter is incompatible:
         25:   on(event: 'foo', listener: Function): this {
                         ^^^^^ string literal `foo`. Expected string literal `foo`
        660:   on(event: string, listener: Function): this;
                         ^^^^^^ string. See lib: /private/tmp/flow/flowlib_15c48d81/node.js:660

[edit]

standalone example on tryflow:

class Foo {
  on(foo: string) {
  }
}

class Bar extends Foo {
  on(foo: 'bar') {
  }
}

Upvotes: 2

Views: 2749

Answers (2)

Aleksey L.
Aleksey L.

Reputation: 37958

You can create an interface with restriction:

class Foo {
  on(foo: string) {
  }
}

interface IBar {
  on(foo: 'bar'): void;
}

class Bar extends Foo implements IBar {
  on(foo: string) {
  }
}

const bar: IBar = new Bar();
bar.on('bar'); //OK
bar.on('foo'); //Error

Upvotes: 1

sushain97
sushain97

Reputation: 2802

Since Bar's on function is overriding Foos, the type of foo in the subclass's method needs to be a subtype of the corresponding type in the superclass's method. While 'bar' is a string, all string are not 'bar'.

For example, these will compile fine:

class Baz {
  on(foo: string|number) {
  }
}

class Bar extends Foo {
  on(foo: string|'bar') {
  }
}

Unfortunately, what you're looking for is not possible in Flow since it has contravariant inputs. The docs have an example to that very effect. I suppose the workaround is to use a wrapper function that simply calls the overloaded version since using generics isn't possible here.

While unrelated, it's interesting to note that support for covariant inputs (what you're looking for) are not common. Wikipedia says Eiffel is the unique "mainstream" language that supports it.

Upvotes: 1

Related Questions