eslint-plugin-flowtype icon indicating copy to clipboard operation
eslint-plugin-flowtype copied to clipboard

Rule for requiring types for fn definitions only

Open amilajack opened this issue 8 years ago • 11 comments

One of Flow's goals was to provide an 'incremental' model, where users could incrementally migrate their code. As a user of this module, I have faced an overwhelming number of issues with the current "flowtype/require-parameter-type" rule, which throws an error on every missing declaration. A rule that would complement flow's goal of 'incremental migration' would be a separate rule for requiring types for function definitions.

Ideally, it would do something like this:

function example(number) {  // <-- require types here
  return new Promise((resolve, reject) => { 
    resolve(someArray.map(each => each)) // <-- DO NOT require types here
  })
}

amilajack avatar Aug 27 '16 00:08 amilajack

There is the option excludeArrowFunctions in both "flowtype/require-parameter-type" and "flowtype/require-return-type". Does that work for you?

opatut avatar Aug 30 '16 09:08 opatut

"flowtype/require-parameter-type" throws way to many errors. For example, it would require the following type declarations in the example:

function example(number) {  // <-- requires types here
  return new Promise((resolve, reject) => { // <-- requires types here
    resolve(someArray.map(each => each)) // <-- requires types here
  })
}

Ideally, there should be a rule that only lints fn definitions or expressions:

function example(number) {  // <-- requires types here only
  return new Promise((resolve, reject) => {
    resolve(someArray.map(each => each))
  })
}

// or its expression equivalent
const example = (number) => {  // <-- requires types here only
  return new Promise((resolve, reject) => {
    resolve(someArray.map(each => each))
  })
}

amilajack avatar Aug 30 '16 14:08 amilajack

@gajus would a PR for adding a "flowtype/require-fn-declarations-expressions-paramater-types" be welcome?

amilajack avatar Aug 30 '16 15:08 amilajack

Personally I don't use the require-* rules as I feel they go against the power of Flow's inference. That said, I can understand the benefit of these rules purely for documentation purposes (although with IDE integration it kinda doesn't matter..).

While in your example the benefit is clear, on this example I think you'd expect type annotations on the inner function:

function blah(a, b) { // would require types
  return function (c, d) { // would not require types, but probably should?
    // ...
  }
}

I think really you'd want some sort of metric to determine whether a function is "simple" (would not require types) or "complex" (would require types).

The current excludeArrowFunctions option probably provides enough differentiation between simple & complex. It could probably do with an additional option for including arrow functions assigned to a variable though.

danharper avatar Aug 30 '16 17:08 danharper

Or perhaps options to include only:

  • function declarations
function x() {} // included
  • arrow functions assigned to variables? (because it's quite common, usually serving the same role as function declarations)
const x = () => {} // included
  • functions being returned (covering factory functions)
function x (a, b) { // included
  return function (x, y) { // included
    // ..
  }
}

function x (a, b) { // included
  return (x, y) => { // included
    // ..
  }
}

const x = (a, b) => { // included
  return (x, y) => { // included
    // ..
  }
}
  • class methods
class X {
  x() {} // included
}

Basically just trying to cover "top-level" functions.

danharper avatar Aug 30 '16 18:08 danharper

Maybe the term you're looking for is "named functions"? I think babel transforms const x = () => {} to var x = function x() {};, so it is actually named.

However, it appears ES6 arrow function have '' as their .name :(

opatut avatar Aug 30 '16 18:08 opatut

@danharper Exactly. @opatut We also want to check anon functions as well.

amilajack avatar Aug 30 '16 20:08 amilajack

Kind of an old ticket, but running into this as well. I'm kind of anal so I want more type annotations than less, but requiring that I annotate Promise(resolve => ...) is a bit much.

Qix- avatar Aug 06 '17 06:08 Qix-

We use

export const moduleFn = (number) => {  // <-- requires types here only
  return new Promise((resolve, reject) => {
    resolve(someArray.map(each => each))
  })
}

We don't want to refactor all our code to use

export const function moduleFn = (number) => ....

A rule that distinguishes exported arrows would be perfect

kvanbere avatar Feb 09 '18 00:02 kvanbere

Another case I've been annoyed by this rule is when I'm overriding a method in a subclass. The superclass already has the parameter type annotated, so it feels silly to have to annotate it again. And frequently the type isn't exported from the superclass.

dmnd avatar Nov 19 '19 18:11 dmnd

I think that requiring types for exported functions is what we need. Inside a module flow inferences all the types.

apsavin avatar Mar 31 '20 06:03 apsavin