[RFC] Base definition modules #4242
Replies: 3 comments 4 replies
-
Will the existence of libraries like
How likely are we to encounter this kind of dependence tree:
I really like the idea of a config file for a definition; I feel like we could really build on that in future projects. 👀
Is this a limitation of the design or something you think wouldn't be very useful? I don't see why we shouldn't allow people to write tests for base definitions, even if we don't require them.
This is really cool, but I wonder how many maintainers would adopt the base definition approach outside of this project. Maybe we can reach out to some of the bigger projects written in Flow/that ship with their own defs and see what they think?
Is there any way, even for just a couple months, that we can release this and still offer the existing definitions? Thinking out loud, I suppose the new version could reference a branch other than master where we'll start the work transitioning a libdef or two to use base definitions. That would give us time to test the idea in the wild and alert users still on V2 and V3 that they need to update...
We should poll the discord and see how many people have been bitten by this issue and could use a solution like this. Not that it's a representative number, but if anytime we could gather some specifics and see how well this would apply. All in a, great RFC and a very interesting solution! 👏🏼 I'll probably have more to add in a day or two. |
Beta Was this translation helpful? Give feedback.
-
I don't fully believe in this solution anymore and I'd like more feedback and ideas. My main concern is that as I've been typing out libs from sindresorhus in particular, a lot of the TS types reuse from his other libs. A base def approach here could work but it would cascade into a list of base defs that all contain one type, alternatively we could have a base def which is called |
Beta Was this translation helpful? Give feedback.
-
Depending on other library definitionsThis is fleshing out an alternative to the original proposal that may suit better given a realisation of how npm packages frequently depend on each other. SolutionDefinitions on particular flow versions that would like to depend on another will have a package.json at the flow dir level.
The contents of this package.json will hold a list of
This allowing for solving the issue of a single libdef being compatible with many versions of of a dependency whether major or minor. With this dependency established, when a consumer installs If they do not have a dependency to redux nor will it be installed by a nested flowtype dependency then we need to install it for Can recursively install dependencies From the repo side to support this change:
|
Beta Was this translation helpful? Give feedback.
-
RFC style inspired by reactjs
Summary
This RFC aims to propose a strategy to handle cross library definition type reuse while maintaining the current cli tooling architecture. This is done through bolting on an ability to add base definition modules where library definitions rely on a list of base (primitive) modules when necessary to share type or variable definitions.
This feature should resolve issues referenced
Current proposed implementation
Basic example
Motivation
In the Javascript ecosystem there are some libraries that serve as a core dependency to other libraries (think react, redux, koa, express, rxjs). These can more or less be called frameworks that are not part of the core javascript feature set yet need a level of reuse across themselves and other libraries.
Lets take a simple example of a popular frontend library
redux
.redux
is a core library that implements an immutable store that can then support both middleware and integration libraries such asredux-thunk
orreact-redux
respectively.Looking at
react-redux
it holds auseDispatch
function which returns adispatch
function which should have the same type definition asdispatch
fromredux
. For this to be in sync, I would currently need to maintain both Dispatch and useDispatch which as you can see they are quite different.useDispatch
is more complicated and has the ability to form better type inference based on whether it's passed a thunk.This highlights one of the major problems of our current system, duplication and desync of type definitions that should have a single source of truth. Which leads library definitions that rely of core libraries with large type definitions to generally be written with
any
to avoid massive unmaintainable duplication and thus a less type safe and less useful tool.Current CLI limitations
Flow itself supports cross definition imports by default no problems as longs as it's able to resolve the import. For example,
As you can see, these will all work and you can try it yourself. Looking closer at
redux
as a flow-typed libdef, the only blocker here for a user would be that if redux wasn't installed, we don't currently have a system that would installredux
for the user if they installedlibdef-a
.Further to this, redux has a few versions 1-4 (which we have 3-4 typed), which version of redux would an installer resolve to if the user installed
libdef-a
?Next if someone were to modify the type definition for a reused type in the flow-typed registry, how do we ensure that a library definition consuming this definition doesn't break? We could run our test suite, though running every single test blindly would be very slow filled with lots of errors from old unmaintained library definitions, so we'll need some way to figure out which definitions were changed, what they impact and add them to the test runner.
The way our tests run right now is that for each definition it runs a test suite per flow version range and injects only the tests and definition file in the package/flow dir.
Detailed design
Based on the information above, the core building block of this solution are base definitions. These are definition modules that live in a new directory
definitions/base
which is a sibling to where current definitions coming from npmjs live (definitions/npm
). Regular library definitions will then rely on these when they need reuse.Rules of a base definition
@flow-typed/base-[module]
.@flow-typed
to avoid name collision from real npm libraries andbase-
to segregate these specific kinds of definitions created byflow-typed
from any other we may create in the future as all definitions under@flow-typed
may not be a base definitions eg:core-[module]
fs
,react
)base-redux_v0.134.x-.js
(this is necessary for specificity of flow versions but also a requirement of the current cli)Once a base definition has been created a library definition must define it as a dependency in a package.json file in the flow version directory. The file will have a single property for now
baseDependencies
which holds an array of the names of the base definition files it will need to depend on.This allows for two things to happen
package.json
's and add the library definitions to the test suite if they depend on this base definition, or if a library definition has been changed we can easily check the package.json to add appropriate base definition for testing.Base definitions do not have tests, they are tested by other definitions and any changes to a base definition would cause their tests to run. They are also considered private and there is no way to install base definitions without installing a definition that first depends on it. With this in mind, it makes base definitions very safe to tweak and overhaul without impacting the end user.
Using base definitions from a library
If I build a library that ships with flow and want to leverage base definitions. Take
redux
again for example, I'm building a library that usesDispatchAPI
originally frombase-redux
, I would need to haveredux
as a dependency first,redux
should expose this type so I can import it directly from there instead of the base definition which could change at any time causing breaking definitions without me as a maintainer doing anything.When I publish this to npm and a user installs my library as a dependency, they will run
flow-typed install
because my library ships with flow it will scan its dependencies to install theredux
library definition which in turn installs thebase-redux
definition.Flow version breakdown
Just like library definitions, base definitions may use syntax that isn't available in older versions of flow so although they don't target package version ranges they will need to target flow version ranges.
If a library depends on a base definition it would need to be broken down to also be compatible with the flow version range it depends on. Here is an example
redux-thunk
is currently typed with a range of0.83.x-
redux
is currently typed with a range of0.83.x.-0.150.x
&0.151-x-
They now depend on
base-redux
which is broken down as0.50.x-0.99.x
&0.100.x-
As the dependency is created
redux-thunk
is forced to be broken down to0.83.x-0.99.x
&0.100.x-
And
redux
will be broken down to0.83.x-99.x
&0.100.x-0.150.x
&0.151.x-
You can think of it this way, just as flow syntax forces you to break down your definition so too does base definitions
Drawbacks
Alternatives
Depending on other library definitions
An easier approach to this solution is instead of adding a new concept of base definitions we could just improve our testing suite to allow changes in one definition to run tests on another dependant definition.
Pros
Cons
redux-thunk
must depend on a specific version ofredux
despite having actual usage being compatible with a range of versionsPublishing to npmjs
Making each definition into an npm package and publishing them with
dependencies
against other definitions. This solution is targeting the movement of installation from npmjs instead of the current cli tool, not a for/against of base definitions.Pros
Cons
flow-typed install
magic commandNot implementing anything
If we don't do anything at all do we gain anything?
Pros
Cons
Adoption strategy
Once the solution is merged as long as no definitions are using base definitions there will be zero impact, as soon as we migrate a single library definition we must release a new breaking v4 version and anyone using them must upgrade to continue using flow-typed.
There should be no reason to not upgrade to newer versions given all recent changes have just been non-breaking enhancements over time that have made the user experience better.
How we teach this
The documentation and mostly the contribution area needs to be updated as part of this change. We should also put something on the main readme page to alert users they must migrate to newest v4 if they are depending on a migrated library definition.
Other than knowledge for maintainers it should be very seamless for consumers who only run
flow-typed install
and go about their lives.Unresolved questions
I have no opinion at all about how we should name a base definition.
For example with
redux
I would think calling itbase-redux
should be fine because between versions the API has remained the same.But with Koa v1 and v2 are different and it would make sense to have
base-koa-1
andbase-koa-2
but also knowing thatkoa@2
is basically stable and I can't imagine a new major version coming out naming itbase-koa
without migratingkoa@1
to use base definitions would also be fine.Also with primitives lets say we want to have a reusable type to describe loose objects
declare export type LooseObject = { [key: string]: any, ... }
. What should we name this base definition? Maybebase-primitives
or something.I really have no preference but overall as mentioned above about how base definitions should be private to library definitions I don't believe we need to have any strict naming convention because if I start with
base-koa
and thenkoa@3
comes out I need to now break it down tobase-koa-2
andbase-koa-3
the tests will break, they'll be fixed and when a user runsflow-typed install
everything will resolve as it should without anyone noticing. Unless they decided to import types directly from a base definition but I also think that's up to their own discretion just like it is if you choose to import a private module inside a library (`import blah from 'redux/lib/secretModule').Beta Was this translation helpful? Give feedback.
All reactions