Flow: `--noImplicitAny` compiler flag / pragma

Created on 4 Mar 2015  路  26Comments  路  Source: facebook/flow

Hi, TypeScript has a --noImplicitAny compiler flag. I find it very useful (it causes a warning to be printed wherever the compiler infers something to be any without being explicitly annotated as any). Are there any plans to add something similar to Flow?

feature request

Most helpful comment

@AriaFallah Thanks for suggestion. But the point here is not "How to Solve the problem" but rather "How to Find the problem". It's quite easy to forget to annotate object literal as map type and we want a way to figure that out.

All 26 comments

This is something I am strongly missing from flow as well. In my app many locations fall back to any and it makes it really hard to see which parts the type checker is certain about and which parts might break when running the app.

In addition to the --noImplicitAny argument proposed by @gcanti, I would also love to have something along the lines of flow --suggest --show-missing-type-information. With this extra flag, flow also creates a diff and at the places, where the type information is imprecise, a comment is added, like:

var fn = function(a /*: implicitAny */) {

} 

This would make it easy to apply the diff and then search in the editor for all instances of /*: implicitAny */ and provide the missing type information for them.

@gabelevi do you think this might be a better issue to get started hacking on the flow type checker than #258?

_ping_ - is there any opinion from the project maintainers on this issue? As said, I would love to implement the --noImplicitAny flag myself if that seems like a reasonable idea to you.

It is really hard to know what is getting properly inferred and what isn't on a large project, particularly when issues show up where types silently stop working: https://github.com/facebook/flow/issues/721

noImplicitAny is the only thing I am missing from TypeScript, but it is a big thing.

this would be really powerful.

It's hard to use an optional type system with rigor if you can slip an any into the type system by accident.

Can someone provide an example where Flow implicitly infers any?

@samwgoldman did you see issue #721 I linked to? I have come across many other similar bugs and not bothered to report them. Maybe someone else can provide some non-bug cases.

721 doesn't really seem related to this. For one, the any isn't implicit鈥攊t's explicit. I agree it is a bug, but not something that the config option here would target.

@samwgoldman

See http://tryflow.org/#098b3c2b6faca656fcec741b6490b4ed and
https://goo.gl/wpDvFN

Basically assigning an instance of a generic class to an untyped variable makes flow interpret it as any

class Box<T> {}
var b = new Box();
/* b is Box<any> */

If had a --noImplicitAny flag then this code would fail type checker and say "cannot create instance of Box without generic type cast" or something.

If you look at the typescript one it looks like it infers "Box<{}>" instead of Box and at least errors on primitives...

@Raynos Also https://github.com/facebook/flow/issues/1513 could help, box could be declared as Box<T: {}> or Box<T: mixed>.

Here is a far far simpler example

var a = [];
a.push(42);
var isStr = '';
isStr = a[0];

Here is what the typescript compiler does:

raynos at raynos-Dell-Precision-M3800  ~/projects/TypeScript on strictNullChecks*
$ tsc ./wat.ts --outDir ./tmp/ --noImplicitAny
wat.ts(8,5): error TS7005: Variable 'a' implicitly has an 'any[]' type.

It knows that a is inferred to be an implicit any[]

@Raynos In neither of your examples does Flow infer any.

/* @flow */

class Box<T> {
  x: T;
  constructor(x: T) {
    this.x = x;
  }
}

var box = new Box();
box.x = 0;
(box.x: string); // error, number is incompatible with string

If x were any, then (box.x: string) would not have been an error.

/* @flow */

var a = [];
a.push(42);
var b: string = a.pop(); // error, number incompatible with string

Again, if a were any[], then it would not have been an error to assign a.pop() to a string.

Rather than any, Flow understands an unannotated type to be "open" meaning it can accumulate lower bounds until it runs into an upper bound.

let x; // x is open
if (b) {
  x = 0; // x gets number lower bound
} else {
  x = ""; // x gets string lower bound
}
// x is number|string
(x: void); // `void` is an upper bound

Another way to look at it: any is unsound. Neither of your examples cause errors. Try to make an example that causes a runtime error.

@samwgoldman

I think I understand more about the semantics of flow now.

/* @flow */

var a = [];
a.push(42);

var isStr = '';
isStr = a[0];
isStr.trim();
  8: isStr.trim();
           ^^^^ property `trim`. Property not found in
  8: isStr.trim();
     ^^^^^ Number

Basically flow will infer a large union which only becomes a type error upon usage rather then a type error upon assignment.

@samwgoldman

There are still implicit any's in the module system.

/* @flow */

// foo does not have /* @flow */ comment
var foo = require('./foo.js');
(foo:number);
(foo:string);

foo()

The module system brings in any as a type and there is no way to say "force strict module system".

Any updates on the chances of adding this / current ways to do this?

I think it makes sense to support the following behaviour with some special pragma @flowStrict:

  • Disallow any/Function/Object in module source.
  • Treat all any/Function/Object types from imported modules as mixed.
  • Report error if module imports any untyped module.

What do you think?

@andreypopp this is exactly what I want. also it can disable some known unsound behaviour

As a work around for lack of pragma you can use a coverage report

https://github.com/rpl/flow-coverage-report

This tool is a lot better then the default flow coverage --color.

The coverage-report tool also has a threshold cli argument that can be used to assert 100% type coverage after running the flow type checker. This will give you high confidence in "zero implicit any" in your code base.

That being said, having this feature in flow itself would improved a better user experience because all of the error messages from the flow command ( as well as text editor integration ) will be consistent. Using a second tool for a coverage report will create two workflows for finding and fixing type errors.

The problem is that coverage can't catch all the problems, it only catches any expressions

I have a very simple example that causes runtime error. Adapted from real code: https://github.com/vuejs/vue/blob/e8d6bd9dc73cacbc665ce132d81a7b629bef764b/src/compiler/codegen/events.js#L78

/* @flow */

const keyCodes = {
  esc: 27,
  tab: 9,
}

function test(b: string) {
  var a = keyCodes[b]
  alert(a.whatever.i.want) // error
}

test('eeee')

I want to check if there is unknown variables in the code base. While I can extract it by flow coverage XXXX.js --json | jq '.expressions .uncovered_locs[] .start .line', but I really want a compiler flag to report them as error.

@HerringtonDarkholme I believe you can fix your problem by using $Keys like this:

const keyCodes = {
  esc: 27,
  tab: 9,
}

function test(b: $Keys<typeof keyCodes>) {
  var a = keyCodes[b]
  alert(a.whatever.i.want) // error
}

test('eeee')

which gives you

x.js:10
 10:   alert(a.whatever.i.want) // error
               ^^^^^^^^ property `whatever`. Property not found in
 10:   alert(a.whatever.i.want) // error
             ^ Number

x.js:13
 13: test('eeee')
     ^^^^^^^^^^^^ function call
 13: test('eeee')
          ^^^^^^ property `eeee`. Property not found in
  8: function test(b: $Keys<typeof keyCodes>) {
                      ^^^^^^^^^^^^^^^^^^^^^^ object literal


Found 2 errors

@AriaFallah Thanks for suggestion. But the point here is not "How to Solve the problem" but rather "How to Find the problem". It's quite easy to forget to annotate object literal as map type and we want a way to figure that out.

This would be super handy. Any update?

Is there an update on this?

Probably this already works? Because flow shows me any (implicit) and any (explicit)

Using the // @flow strict pragma and at least the following config in .flowconfig:

[strict]
unclear-type
untyped-import
untyped-type-import

has ensured in my case that the only implicitly any-typed code that is left are caught exceptions.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

blax picture blax  路  83Comments

opensrcery picture opensrcery  路  88Comments

gcanti picture gcanti  路  48Comments

Gozala picture Gozala  路  54Comments

cletusw picture cletusw  路  52Comments