最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

flowtype - Is it possible to use a type which would represent many class types? - Stack Overflow

programmeradmin4浏览0评论

I'm facing a problem with Facebook Flow, briefly stated in the title of the question. The reason is that I don't want to specify Class<Bird> | Class<Snake> 100 times.

Here is a simplified example:

There are many classes which all implement a particular interface Animal. I need to be able to specify, that, let's say, a function accepts values of type T and those values are classes which implement Animal. That is if I call return new c(); in the function (as in the code snippet below), its return type will be T, an instance of a class which implements Animal;

Currently, I'm getting this error. But I've tried numerous other approaches: type assertions, object types with intersection operators etc. Those didn't work either.

//@flow strict

interface Animal {
  eat(): void,
}

class Dog implements Animal {
  constructor() { }
  eat() { }
  bark() { }
}

foo(Dog);

function foo(c: Class<Animal>) {
  return new c();     ■ Cannot call `c` because property `constructor` is missing in  `Animal` [1].
}

Is this possible to do? Even if it will be to skip somehow type checks in this particular place.

I'm facing a problem with Facebook Flow, briefly stated in the title of the question. The reason is that I don't want to specify Class<Bird> | Class<Snake> 100 times.

Here is a simplified example:

There are many classes which all implement a particular interface Animal. I need to be able to specify, that, let's say, a function accepts values of type T and those values are classes which implement Animal. That is if I call return new c(); in the function (as in the code snippet below), its return type will be T, an instance of a class which implements Animal;

Currently, I'm getting this error. But I've tried numerous other approaches: type assertions, object types with intersection operators etc. Those didn't work either.

//@flow strict

interface Animal {
  eat(): void,
}

class Dog implements Animal {
  constructor() { }
  eat() { }
  bark() { }
}

foo(Dog);

function foo(c: Class<Animal>) {
  return new c();     ■ Cannot call `c` because property `constructor` is missing in  `Animal` [1].
}

Is this possible to do? Even if it will be to skip somehow type checks in this particular place.

Share Improve this question edited Mar 17 at 16:47 jabaa 6,9983 gold badges15 silver badges39 bronze badges asked Mar 17 at 16:30 d.kd.k 4,4702 gold badges32 silver badges41 bronze badges 7
  • I don't see what's the problem. A type that represents many types could be 1) the common base class of all those types, 2) some interface type if all the types in question implement this interface. Everything else depends on what you are going to do with this type. The usual approach is to use this type as a compile-time (abstraction) type to handle a polymorphic set of instances of your many types. And this is the very heard of OOP. – Sergey A Kryukov Commented Mar 17 at 17:06
  • Another hint: fet generics and add a static factory method to a common base class. This factory method should call a constructor in a type-agnostic way. This is like saying: create an instance of your class, whatever it is. – Sergey A Kryukov Commented Mar 17 at 17:09
  • @SergeyAKryukov thank you for the reply. May I ask you what did you mean by the 'type agnostic way'? It's not clear, how to do it – d.k Commented Mar 17 at 17:16
  • The problem with the base class approach, as I understand, is that you can't use it as a type representing all other classes. Flowtype will treat it as an instance of that base class, not an instance of any of its child classes. – d.k Commented Mar 17 at 17:29
  • Wrong. The base class does represent all derived classes, and this is the very heart of OOP and its major point. In particular, do you know how late binding works? – Sergey A Kryukov Commented Mar 17 at 17:56
 |  Show 2 more comments

2 Answers 2

Reset to default 0

My current solution is to ignore that error.

And my conclusion is that it's not only justified in this situation, but it's the correct way. Because the Flow documentation explicitly mentions, that Flow is not expected to handle every possible syntax construct (sounds pretty reasonable, since it's main benefit seems to be to statically define certain "contracts" between parts of an application, not to be a sort of "static runtime" engine for types).

Flow reports many different kinds of errors for many common programming mistakes, but not every JavaScript pattern can be understood by Flow.

So in my case I just added the comment and declared the return type:

function foo(c: Class<Animal>): Animal {
  //$FlowExpectedError[prop-missing]
  return new c();
}

In Facebook Flow, the issue arises because Class<Animal> suggests that Animal should be instantiated, but interfaces in Flow don't have a constructor. You need to ensure that the class type parameter correctly extends Animal while keeping the constructor accessible.

Corrected Code

// @flow strict

interface Animal {
  eat(): void,
}

class Dog implements Animal {
  constructor() {}
  eat() {}
  bark() {}
}

function foo<T: Animal>(c: Class<T>): T {
  return new c();
}

const dogInstance = foo(Dog);
dogInstance.eat(); // Works fine

Explanation

  • T: Animal ensures that T is a subtype of Animal.

  • Class<T> ensures that T is a class constructor.

  • return new c(); now correctly returns an instance of the provided class.

This approach avoids repeating multiple class types (Class<Bird> | Class<Snake> etc.) and ensures proper type safety.

For reference, Microsoft primarily focuses on TypeScript rather than Flow. However, you can check Flow's official documentation for Class<T> and type constraints:

  • Reference:
    Flow Docs - Class

    Flow Docs - Generics

发布评论

评论列表(0)

  1. 暂无评论