Giter VIP home page Giter VIP logo

babel-plugin-typecheck's Introduction

Babel Typecheck

This is a Babel plugin for static and runtime type checking using flow type annotations.

Build Status

Deprecated

This project is now deprecated, we're now focussing on flow-runtime.


Note: Now requires babel 6.1, babel 5 users see the 2.x branch.

What?

Turns code like this:

function sendMessage (to: User, message: string): boolean {
  return socket.send(to, message);
}

into code like this:

function sendMessage(to, message) {
  var _socket$send;

  if (!(to instanceof User)) throw new TypeError("Value of argument 'to' violates contract.");
  if (typeof message !== "string") throw new TypeError("Value of argument 'message' violates contract.");
  _socket$send = socket.send(to, message);
  if (typeof _socket$send !== "boolean") throw new TypeError("Function 'sendMessage' return value violates contract.");
  return _socket$send;
}

And guards against some silly mistakes, for example the following code will fail to compile with a SyntaxError, because the function can return the wrong type.

function foo (): boolean {
  if (Math.random() > 0.5) {
    return "yes"; // <-- SyntaxError - string is not boolean
  }
  else {
    return false;
  }
}

function bar (input: string = 123): string { // <-- SyntaxError: default value is not string
  return input + "456";
}

Installation

First, install via npm.

npm install --save-dev babel-plugin-typecheck

Then, in your babel configuration (usually in your .babelrc file), add "typecheck" to your list of plugins:

{
  "plugins": [
    ["typecheck", {
      "disable": {
        "production": true
      }
    }]
  ]
}

The example configuration will disable typecheck when NODE_ENV=production which is usually preferable for performance reasons.

Important: This plugin has a dependency on babel-plugin-syntax-flow and babel-plugin-transform-flow-strip-types. Without syntax-flow, babel will be unable to parse the flow annotation syntax. Without transform-flow-strip-types, the type annotations will be included in the output which will make it unparsable by JS engines.

If you are not already using the babel-preset-react plugin, you must install those plugins and include them in your babel configuration (usually .babelrc). Put them after typecheck in the list, e.g.

{
  "plugins": ["typecheck", "syntax-flow", "transform-flow-strip-types"]
}

If you are using babel-preset-react you can ignore this warning.

Note Depending on your babel configuration you may encounter issues where typecheck interferes with other transformations. This can almost always be fixed by adjusting your preset order and setting "passPerPreset": true in your .babelrc.

Examples

The basic format is similar to Flow Type Annotations.

Here are a few examples of annotations this plugin supports:

function foo(
    aNum: number,
    anOptionalString: ?string, // will allow null/undefined
    anObject: Object,
    aDate: Date,
    anError: Error,
    aUnionType: Object|string,
    aClass: User,
    aShape: {foo: number, bar: ?string},
    anArray: Array,
    arrayOf: string[] | Array<string>,
    {x, y}: {x: string, y: number}, // destructuring works
    es6Defaults: number = 42
) : number {
  return aNum;
}

Importing and Exporting types.

You can reuse types across modules using an extension of the ES6 module syntax:

places.js:

export type CsvDataType = Array<Array<String>>;
export type LocationType = {
    country: string,
    sourceNid: string,
    locationNid: string,
    name: string,
    url: string,
    alternativeUrl: ?string,
    street1: ?string
};

widget.js:

import type {
    CsvDataType,
    LocationType
} from './places';

// You can now use CsvDataType and LocationType just like any other type.

Note that in contrast to flow, an imported type must be an actual type and cannot be a class or other concrete value.

Optimization

In cases where typecheck can statically verify that the return value is of the correct type, no type checks will be inserted, for instance:

function bar (): string|Object {
  if (Math.random() > 0.5) {
    return "yes";
  }
  else {
    return {
      message: "no"
    };
  }
}

will produce no type checks at all, because we can trivially tell that the function can only return one of the two permitted types. This is also true for simple cases like:

function createUser (): User {
  return new User(); // <-- no typecheck required
}

This is currently quite limited though, as the plugin can only statically infer the types of literals and very simple expressions, it can't (yet) statically verify e.g. the result of a function call. In those cases a runtime type check is required:

function createUser (): User {
  return User.create(); // <-- produces runtime typecheck
}

Changes in 3.5.0

Supports various number types:

  • int8
  • uint8
  • int16
  • uint16
  • int32
  • uint32
  • float32
  • float16

Example:

function demo (input: uint8): uint16 {
  return input * input;
}

demo(1); // ok
demo(128); // ok
demo(255); // ok
demo(-1); // TypeError
demo(12.34); // TypeError
demo(1024); // TypeError
demo('nope'); // TypeError

Changes in 3.0.0

Supports type aliases:

type Foo = string|number;

function demo (input: Foo): string {
  return input + '  world';
}

demo('hello'); // ok
demo(123); // ok
demo(["not", "a", "Foo"]); // fails

Better static type inference

function demo (input: string): string[] {
  return makeArray(input); // no return type check required, knows that makeArray is compatible
}

function makeArray (input: string): string[] {
  return [input];
}

Type propagation

function demo (input: string): User {
  const user = new User({name: input});
  return user; // No check required, knows that user is the correct type
}

Assignment tracking

let name: string = "bob";

name = "Bob"; // ok
name = makeString(); // ok
name = 123; // SyntaxError, expected string not number

function makeString (): string {
  return "Sally";
}

Type casting

let name: string = "bob";

name = "Bob";
((name: number) = 123);
name = 456;
name = "fish"; // SyntaxError, expected number;

Array type parameters

function demo (input: string[]): number {
  return input.length;
}

demo(["a", "b", "c"]); // ok
demo([1, 2, 3]); // TypeError

Shape tracking

type User = {
  name: string;
  email: string;
};

function demo (input: User): string {
  return input.name;
}

demo({}); // TypeError
demo({name: 123, email: "[email protected]"}); // TypeError
demo({name: "test", email: "[email protected]"}); // ok

Pragmas

Sometimes you might need to disable type checking for a particular file or section of code. To ignore an entire file, add a comment at the top level scope of the file:

// typecheck: ignore file
export function wrong (input: string = 123): boolean {
  return input + ' nope';
}

To ignore a particular statement:

let foo: string = "hello world";
// typecheck: ignore statement
foo = 123;

Note: Because of how typecheck works, it's not possible to ignore individual lines, only entire statements or files. So if you ignore e.g. an if statement, the entire body of that statement will be ignored.

You can also control the disabling and enabling of type checking using the plugin options and the @typecheck pragma. Type checking will be enabled only for files where any of the configured only values are found in the @typecheck pragma. With babel configuration:

"plugins": [
  ["typecheck", { only: ["production", "test"] }],
  ...
  ]

This file would have typechecks enabled

// @typecheck: production, some

Whereas this file would not:

// @typecheck: any, some

License

Published by codemix under a permissive MIT License, see LICENSE.md.

babel-plugin-typecheck's People

Contributors

fixplz avatar johnydays avatar motiz88 avatar panrafal avatar phpnode avatar sakari avatar strml avatar tannerrogalsky avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

babel-plugin-typecheck's Issues

When custom constructor type is used, it silently fails

When custom constructor type is used, it silently fails:

import { Map as $Map } from 'immutable';

export default function dummiesReducer(
  $state: $Map = $initialState,
  {
    type,
    entities,
  } : {
    type    : string,
    entities: ?$Map, // <--- Object is passed here
  }
) {
  // silently fails here...
}

Broken code emitted for rest parameters

The emitted type check for args in the following function references it as ...args which results in a syntax error.

function countArgs(...args: Array<number>): number
{
    return args.length;
}

Will follow up with a PR in a sec :)

Some ideas about type reflection

What about reflection types in runtime, for example:

function myFn(a: string): string {
 return a + '1'
}

transpiled to

import t from 'tcomb'

function myFn(a) {
  myFn.__$.in(a)
  var res = a + '1'
  myFn.__$.out(res)
  return res
}

myFn.__$ = {
  in: t.Str
  out: t.Str
}

Now we can easy parse types in runtime and extract some info, this bridge for React.proptypes. We can easily build plugins for working with types in runtime.

class AnyComponent extends React.Component {
   propTypes: {
      a: string
   }
}

transpile to

import t from 'tcomb'
class Any {
  $schema: t.struct({a: t.Str})
}

What about using https://github.com/gcanti/tcomb for type checking?
Why tcomb? Because it's great runtime type specification. It's powerfull type assertion library with many types, which you reimplement internally in you plugin.

Refactor

The rewrite for 3.0.0 ended up growing in scope much more than I originally planned so now the code is a terrible mess. This issue is a reminder to clean it up!

Impassable excess tests

Input:

let Component = createClass({
    constructor(block, options) {
        if (options.init) {
            this.init = options.init;
        }
        if (options.dispose) {
            this.dispose = options.dispose;
        }

        block[KEY_COMPONENT] = this;
    },

    init: null,
    dispose: null
});

Output:

var Component = createClass({
    constructor: function constructor(block, options) {
        if (options.init) {
            this.init = options.init;

            if (!(this.init == null)) {
                throw new TypeError('Value of "this.init" violates contract, expected void got ' + (this.init === null ? 'null' : _typeof(this.init) === 'object' && this.init.constructor ? this.init.constructor.name || '[Unknown Object]' : _typeof(this.init)));
            }
        }
        if (options.dispose) {
            this.dispose = options.dispose;

            if (!(this.dispose == null)) {
                throw new TypeError('Value of "this.dispose" violates contract, expected void got ' + (this.dispose === null ? 'null' : _typeof(this.dispose) === 'object' && this.dispose.constructor ? this.dispose.constructor.name || '[Unknown Object]' : _typeof(this.dispose)));
            }
        }

        block[KEY_COMPONENT] = this;

        if (!(block[KEY_COMPONENT] != null && _typeof(block[KEY_COMPONENT]) === 'object' && typeof block[KEY_COMPONENT].constructor === 'function' && block[KEY_COMPONENT].init == null && block[KEY_COMPONENT].dispose == null)) {
            throw new TypeError('Value of "block[KEY_COMPONENT]" violates contract, expected { constructor(): any;\n  init: void;\n  dispose: void;\n} got ' + (block[KEY_COMPONENT] === null ? 'null' : _typeof(block[KEY_COMPONENT]) === 'object' && block[KEY_COMPONENT].constructor ? block[KEY_COMPONENT].constructor.name || '[Unknown Object]' : _typeof(block[KEY_COMPONENT])));
        }
    },

    init: null,
    dispose: null
});

All three added tests will always throw an error.

Define object with props as an optional argument

Thanks for this plugin! I sleep much better now :)

I have 3 questions. Will split them in 3 issues. Here is first one.

Is it possible to define object with props as an optional argument?

export default function dummiesReducer(
  $state: $Map = $initialState,
  {
    type,
    index,
    errors,
  } : {
    type  : string,
    index : ?Array<number>,
    errors: ?{ code: number, data: Object }, // <--- if arg is passed - check its props
  }
) {
  // ...
}

Support for eg Array<string>?

Hi,

Flowtypes apparently supports annotations like Array, which checks that only string values are included in the array. Currently this plugin ignores the type parameter and just checks that the value is an array (so eg you can pass ["abc", 1] to a function that takes Array). Are there any plans to support type parameters?

Reach full compatibility with flow

So there is the choice when an unsupported feature or annotation is encountered between ignoring and showing a warning (I didn't want to investigate if there is a non-error way of showing a message through Babel so I opted for throwing).

Silently ignoring annotations has the downside of being confusing for users. I don't know what different people look for in this plugin but likely many will want something simple and predictable without having to learn how Flow works. If we cater for this case then it's important to warn users about cases where no type check will be performed despite an annotation being present.

Flow rejects language features it doesn't support - the idea is to provide a very good guarantee that the program will work or give an error. So you can't use this plugin together with Flow to cover cases it doesn't support. If a Flow check succeeds then there will be no type error exceptions of the kind our plugin produces.

From our side silent failures are contrary to how Flow is meant to work.

So I want to understand your aim regarding this - how should this be compatible with Flow?

Syntax error when using callback in generic function definition with flowtype annotation

export function styleWithSetting(setting: RequiredSetting, callback: (value: T) => number): number {
...
}

SyntaxError: layout.js: Line 82: Unsupported type annotation type: FunctionTypeAnnotation

SyntaxError: /Users/peter/Developer/js/src/lib/components/layout.js: Line 82: Unsupported type annotation type: FunctionTypeAnnotation
80 | }
81 |

82 | function styleWithSetting(setting: RequiredSetting, callback: (value: T) => Style): Style {
| ^
83 |
84 | let style: any = {};
85 |
at File.errorWithNode (/Users/peter/Developer/js/node_modules/babel-core/lib/transformation/file/index.js:489:13)
at NodePath.errorWithNode (/Users/peter/Developer/js/node_modules/babel-core/lib/traversal/path/index.js:155:26)
at NodePath.Function (/Users/peter/Developer/js/node_modules/babel-plugin-typecheck/lib/index.js:48:22)
at NodePath.call (/Users/peter/Developer/js/node_modules/babel-core/lib/traversal/path/context.js:56:28)
at NodePath.visit (/Users/peter/Developer/js/node_modules/babel-core/lib/traversal/path/context.js:90:8)
at TraversalContext.visitMultiple (/Users/peter/Developer/js/node_modules/babel-core/lib/traversal/context.js:108:16)
at TraversalContext.visit (/Users/peter/Developer/js/node_modules/babel-core/lib/traversal/context.js:146:19)
at Function.traverse.node (/Users/peter/Developer/js/node_modules/babel-core/lib/traversal/index.js:76:17)
at NodePath.visit (/Users/peter/Developer/js/node_modules/babel-core/lib/traversal/path/context.js:107:26)
at TraversalContext.visitSingle (/Users/peter/Developer/js/node_modules/babel-core/lib/traversal/context.js:132:12)
at TraversalContext.visit (/Users/peter/Developer/js/node_modules/babel-core/lib/traversal/context.js:148:19)

Question regarding to production mode optimisation.

I have some questions regarding to have babel-plugin-typecheck running in production mode. Is there any way that I can cut off the overheads of type checking in production mode, which means the type check will be enable in development only. My current config for .babelrc is:

{
  "stage": 0,
  "plugins": ["typecheck"],
  "optional": ["runtime"]
}

And my production config I include new webpack.optimize.UglifyJsPlugin(). However, I got the production code compiled to the following:

      if ("number" != typeof t) throw new TypeError("Value of argument 'a' violates contract, expected number got " + (null === t ? "null" : t instanceof Object && t.constructor ? t.constructor.name : typeof t));
      if ("number" != typeof e) throw new TypeError("Value of argument 'b' violates contract, expected number got " + (null === e ? "null" : e instanceof Object && e.constructor ? e.constructor.name : typeof e));

Any idea on this? Also thanks for this wonderful plugin.

Doesn't work with babel require hook

I have the following in foo.js:

require('babel-core/register')({
  'plugins': ['typecheck']
})

require('./bar')

In bar.js:

export default function () {}

Error:

TypeError: template is not a function
    at createChecks (/Users/me/workspace/hello-world/node_modules/babel-plugin-typecheck/lib/index.js:322:15)
    at exports.default (/Users/me/workspace/hello-world/node_modules/babel-plugin-typecheck/lib/index.js:27:16)
    at Function.memoisePluginContainer (/Users/me/workspace/hello-world/node_modules/babel-core/lib/transformation/file/plugin-manager.js:77:23)
    at PluginManager.add (/Users/me/workspace/hello-world/node_modules/babel-core/lib/transformation/file/plugin-manager.js:209:30)
    at File.buildTransformers (/Users/me/workspace/hello-world/node_modules/babel-core/lib/transformation/file/index.js:237:21)
    at new File (/Users/me/workspace/hello-world/node_modules/babel-core/lib/transformation/file/index.js:139:10)
    at Pipeline.transform (/Users/me/workspace/hello-world/node_modules/babel-core/lib/transformation/pipeline.js:164:16)
    at Object.transformFileSync (/Users/me/workspace/hello-world/node_modules/babel-core/lib/api/node.js:137:37)
    at compile (/Users/me/workspace/hello-world/node_modules/babel-core/lib/api/register/node.js:132:20)
    at normalLoader (/Users/me/workspace/hello-world/node_modules/babel-core/lib/api/register/node.js:199:14)

Not returning a value

This is my .babelrc config file:

{
  "plugins": [
    "typecheck",
    "transform-async-to-generator"
  ]
}

while this is my code:

'use strict'

const Koa = require('koa')
const app = new Koa()

// define logger - this will be always executed
const logger = async (context, next) => {
  const start = new Date()
  await next()
  const ms = new Date() - start
  console.log(`${context.method} ${context.url} - ${ms}ms`)
}

// this will be always executed
const index = (context) => {
  context.body = foo()
}

function foo (): boolean {
  if (Math.random() > 0.5) {
    return true
  } else {
    return false
  }
}

app.use(logger)
app.use(index)

app.listen(3000)
console.info(`The app is listening on port 3000`)

whatever function I try to verify I get always an error like this:

Function "foo" did not return a value, expected bool

I have tried with different function but always a similar error.

Saving type as a property

I'm using React and would like to define a type for state.

here is how I define it:

, getInitialState() {
    type stateType = {
      someStateVar: number;
    }
    this.stateType = stateType    //saving type for mixin

    return this.typedState({
      someStateVar: 3
    })
  }

and this is my state mixin:

var StateTypeMixin = {
  handleSetState(s) {
    this.setState(this.typedState(s))
  }
, typedState(s) {
    var newState = Object.assign({}, this.state, s)
    var stateType = this.stateType    //getting state
    var typedState : stateType = newState
    return typedState
  }
}

It works when I copy type stateType = .. to typedState mixin function where it is used.

Otherwise I get the following error:

Uncaught TypeError: Value of variable "typedState" violates contract, expected stateType got Object

Even tho in console.log(stateType) it shows what seems to be the same function from both places

stateType(input) {
    return input != null && (typeof input === 'undefined' ? 'undefined' : _typeof(input)) === 'object' && typeof input.someStateVar === 'number';
}

and the stateType(newState) returns true.

Same happens when I pass the type as argument:

, getInitialState() {
    type stateType = {
      someStateVar: number;
    }
    return this.typedState({
      someStateVar: 3
    }, stateType)

Any ideas?
Thanks

Promises?

Promise support would be great:

TypeError: Function 'loadCases' return value violates contract, expected Promise got Promise

Conditional comment to ignore syntax?

Thanks for the great work on this plugin.

As I convert my project to Flow, I've been noticing some incompatibilities between the two libraries.

Some of this is because I am still stuck on Babel 5 / Typecheck 2.0.0 due to react-transform not yet releasing a Babel 6 plugin.

For example, the following definition is valid Flow but throws in plugin-typecheck:

function(symbol: string): [?number, ?string] { /* ... */ }

The error is:

SyntaxError: Unsupported annotation type: TupleTypeAnnotation

For now I have commented the line, set the type to any and put a big FIXME on it until I am able to update to the newest typecheck lib. But in case this happens again in the future (and I assume it will, as Flow receives new features), is there a way for us to get typecheck to simply ignore a line?

Exported Types

Having my type declared as:

// myType.es6
export type Type = {
  id:  number,
};

I'd want to use it this way:

// myImplem.es6
import type {Type} from 'myTypeFile';

export function get(id) : Type {
  let j: Type = { id };
  // do stuff
  return j;
}

but these respectively transform into:

// myType.js
"use strict";

exports.__esModule = true;
// myImplem.js
'use strict';

exports.__esModule = true;
exports.get = get;

function get(id) {
  var j = {
   id: id.
  };

  if (!(j instanceof Type)) throw new TypeError('Function \'get\' return value violates contract, expected Type got ' + (j === null ? 'null' : j instanceof Object && j.constructor ? j.constructor.name : typeof j));
  return j;
}

Which quite logically produces this error at runtime.

ReferenceError: Type is not defined

Any idea of whether it is more likely to be an error in my configuration or a bug in the tool ?

Objects with unknown props: Immutability check

One of 2 main things I try to solve is to check:

  1. Is type of passed id String or Number.
  2. Is type of passed object is Plain JS Object or Immutable.js instance.

First one works great, but second one is tricky in case when I check Object with unknown set of props:

// Check Immutable instance
// Should work like this (except issue#40)
function dummiesReducer($immutableMap: $Map) {}

// Check Object with known props is also trivial
function dummiesReducer(obj: { knownProp: string }) {}

// But I'm not sure what's the best way to check
// that passed object with unknown props is Plain JS Object
// and not an Immutable.js instance
function dummiesReducer(obj: Object) {} // <--- Won't work as Immutable.js instance is also Object

// For now I ended up with this hack
function dummiesReducer(obj: { toJS: void }) {}

Is there any better way to perform such checks?

[Question] React PropTypes support

I acknowledge this might be out of scope for babel-plugin-typecheck but what about React PropTypes?

I would expect following example to fail static type check.

const Component = React.createClass({
  propTypes: {
    name: React.PropTypes.string.isRequired
  },
  render() {
    return <div>{this.props.name}</div>;
  }
});

const App = React.createClass({
  render() {
    return <Component name={42} />;
  }
});

Could you please give some insight on if/how it is possible do static check for react components (runtime check is performed by React itself though)?

The reason to have a static check is to be able to deal with higher kind of types. Sort of (well, syntax could be different for correct static type checks):

const Name = React.PropTypes.string;
const User = React.PropTypes.shapeOf({
  name: Name.isRequired
});


const Component = React.createClass({
  propTypes: {
    user: User.isRequired
  },
  render() {
    return <div>{this.props.user.name}</div>;
  }
});

const App = React.createClass({
  render() {
    var user = {name: 'Test'};
    // I suppose this to fail since plain Object is not type of User
    return <Component user={user} />;
  }
});

PS: feel free to close the ticket if it is really out of scope.

Function expression with return type triggers token error

let sendMessage = (to: User, message: string): boolean => {
  return socket.send(to, message);
}

Compilation returns error:

SyntaxError: example.js: Unexpected token (1:45)
> 1 | let sendMessage = (to: User, message: string): boolean => {
    |                                              ^
  2 |   return socket.send(to, message);
  3 | }
  4 |

It compiles fine without the return type restriction.

Type annotations in source are out of date

What is the status with the type annotations in the type checker's source?

Self-type-checking is currently disabled and enabling it makes the code fail to run, mainly because Babel no longer exposes the Scope type.

I propose we change the package scripts to this and fix the problems, so the annotations stay functional:

"scripts": {
  "build": "babel -d ./lib ./src",
  "build-typed": "babel --plugins typecheck -d ./lib ./src",
  "prepublish": "npm run build",
  "pretest": "npm run build-typed",
  "test": "mocha",
  "watch": "mocha --watch"
},

v3.2.0 default parameters

The 3.2.0 update seems to have messed up default parameters in some way. Specifically, the following example worked before the update, and fails after the update:

function foo(options?: {
        option1?: string,
        option2?: boolean
    } = { })
{
    // ...
}

The error message is:

Invalid default value for argument "options", expected { option1?: string;
  option2?: bool;
}.
  29 |         option1?: string,
  30 |         option2?: boolean
> 31 |     } = { })
     |       ^
  32 | {
  33 |
  34 | }

I have not tested more basic default parameter examples.

Iterator support?

I'm converting my codebase from Babel 5 to 6 and was wondering whether the Map, Set, Iterator and other such types were supported? I get the following Iterable is not defined error with the following annotation:

methodName(patterns: Iterable<string | Array<string>>) {
 // content.
}

Thank you!

Support for `Class` and `Class<T>` annotations

Currently we can only specify a naive check of Function or Function<T>. If we specify a type of Class the plugin treats it as a variable.

Given:

class A {
  _parent: A
  setParent(p: Parent): void {
    this._parent = p;
    // Do other stuff...
  }
}
class B extends A { }

It would be nice if one could express:

let factoryFn = (type: Class<A>, parent: A): A => {
  let instance = new type();
  instance.setParent(parent); // Some DI stuff happens here (not really important)
  return instance;
}

So factoryFn would:

  • check that the type argument was a the class definition A or one of it's sub-classes
  • check the parent argument is an instance of A or one of it's sub-classes

Hope this makes sense, cheers!

Object typecheck has no method 'apply'

Hi-
I'm getting this error when "typecheck" is one of the webpack plugins. Any ideas?

13:25:13 web.1 | /Users/jeff.winkler/repos/project/node_modules/webpack/node_modules/tapable/lib/Tapable.js:164
13:25:13 web.1 | arguments[i].apply(this);
13:25:13 web.1 | ^
13:25:13 web.1 | TypeError: Object typecheck has no method 'apply'
13:25:13 web.1 | at Tapable.apply (/Users/jeff.winkler/repos/project/node_modules/webpack/node_modules/tapable/lib/Tapable.js:164:16)
13:25:13 dashv1.1 |
13:25:13 web.1 | at OptionsApply.WebpackOptionsApply.process (/Users/jeff.winkler/repos/project/node_modules/webpack/lib/WebpackOptionsApply.js:64:18)
13:25:13 dashv1.1 | /Users/jeff.winkler/repos/project/node_modules/webpack/node_modules/tapable/lib/Tapable.js:164
13:25:13 web.1 | at webpack (/Users/jeff.winkler/repos/project/node_modules/webpack/lib/webpack.js:22:48)
13:25:13 dashv1.1 | arguments[i].apply(this);
13:25:13 web.1 | at Object. (/Users/jeff.winkler/repos/project/server.js:8:22)
13:25:13 dashv1.1 | ^
13:25:13 web.1 | at Module._compile (module.js:456:26)
13:25:13 web.1 | at Object.Module._extensions..js (module.js:474:10)
13:25:13 web.1 | at Module.load (module.js:356:32)
13:25:13 web.1 | at Function.Module._load (module.js:312:12)
13:25:13 web.1 | at Function.Module.runMain (module.js:497:10)
13:25:13 web.1 | at startup (node.js:119:16)
13:25:13 web.1 | exited with code 8

Union of literals

With the following code:

type EnumType = 't1' | 't2';

function f(b: EnumType) {
}

f('t3');

I don't receive a warning using Flowtype. Using it with this plugin, it is transformed into a function that simply checks that the given variable b is of a type of one of the literals in the EnumType. I think it should not just check for the type when the type alias is using literals but also compare it to the given values.

Based off of reading some of the Flowtype issues, unions are the only way of defining enums currently.

Invalid Syntax When using Default Values for Paramters

I guess I just found a weird corner case. Using default parameter values with types generates invalid syntax.

function lol(settings={}: Object) {}
lol({x: 1})

becomes:

"use strict";

function lol() {
  var settings = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
  if (typeof settings = {} !== "object") throw new TypeError("Value of argument 'undefined' violates contract, expected object got " + (settings = {} === null ? "null" : settings = {} instanceof Object && settings = {}.constructor ? settings = {}.constructor.name : typeof settings = {}));
}
lol({ x: 1 });

which doesn't work because:

/…/stack.es5.js:5
  if (typeof settings = {} !== "object") throw new TypeError("Value of argumen
  ^
ReferenceError: Invalid left-hand side in assignment
    at lol (/…/stack.es5.js:5:3)
    at Object.<anonymous> (/…/stack.es5.js:7:1)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:935:3

Cannot read property 'kind' of undefined

$ babel --version
5.4.2
$ cat test.jsx

class Foo {
    bar(v : string) {
    }
}

$ babel --plugins typecheck test.jsx
/babel/lib/babel/transformation/file/index.js:600
throw err;
^
TypeError: test.jsx: Cannot read property 'kind' of undefined
at Scope.getAllBindingsOfKind (
/babel/lib/babel/traversal/scope/index.js:824:22)
at TraversalPath.Function (/node_modules/babel-plugin-typecheck/lib/index.js:41:30)
at TraversalPath.call (
/babel/lib/babel/traversal/path/index.js:833:28)
at TraversalPath.visit (/babel/lib/babel/traversal/path/index.js:866:10)
at TraversalContext.visitSingle (
/babel/lib/babel/traversal/context.js:78:43)
at TraversalContext.visit (/babel/lib/babel/traversal/context.js:89:19)
at Function.traverse.node (
/babel/lib/babel/traversal/index.js:64:17)
at TraversalPath.visit (/babel/lib/babel/traversal/path/index.js:883:28)
at TraversalContext.visitMultiple (
/babel/lib/babel/traversal/context.js:67:16)
at TraversalContext.visit (~/babel/lib/babel/traversal/context.js:87:19)

Better Errors

It would be really useful if the run-time checks can throw more meaningful errors. As a simple improvement, it might be a good step to say what the contract was in the error message rather than say that the type does not match the contract.

Type annotation in loop

In the following example:

let foo: Array<string> = [ ];

for (let bar: string of foo)
{
    // ...
}

I get a runtime error that bar violates its contract as it expected string got undefined. This only happens when foo is an empty array, but it seems a bit silly for this to occur. Running the same code through Flowtype does not produce a warning or error.

It is easy enough to just use ?string but this requires an additional null/undefined check before using bar so that Flowtype will not complain about it as well.

Broken code emitted for conditional return statements

With 1.3.0 and the latest Babel, I encountered this severely broken behavior where some of my functions were returning undefined instead of the expected value, but only with typecheck enabled. I've tracked it down to the following minimal case.

The source

function a(): number
{
    if (true)
        return [].length;
}

is transpiled to

function a() {
    var _length; // Never initialized!

    if (true) return _length;
}

I'm working on a PR with at least a test, and at best a fix if I can manage it.

Are objects in class constructors supported?

The plugin works perfectly with functions. Unfortunately I ran to a glitch with class constructors. To keep it simple I have a declaration like this:

export default class Lane extends React.Component {
  constructor(props: {
    name: string;
    i: number;
  }) {
  ...
  }
}

If I pass something else than string as a name, it doesn't fail as I expect. I know flowcheck can deal with this case but I'm actually looking at your tool as a replacement.

You can find a runnable example at https://github.com/survivejs/webpack_react/tree/typecheck-glitch .

Let me know if you need further information.

Object #<Scope> has no method 'generateUidBasedOnNode'

Hi-
Starting getting this error, any ideas? Happened after rebuilding local node_modules dir.

11:24:00 web.1    | ERROR in ./components/editor/Editor.js
11:24:00 web.1    | Module build failed: TypeError: /Users/jeff.winkler/repos/project/components/editor/Editor.js: Object #<Scope> has no method 'generateUidBasedOnNode'
11:24:00 web.1    |     at createReferenceTo (/Users/jeff.winkler/repos/project/node_modules/babel-plugin-typecheck/lib/index.js:254:22)
11:24:00 web.1    |     at TraversalPath.exitNode (/Users/jeff.winkler/repos/project/node_modules/babel-plugin-typecheck/lib/index.js:307:17)
11:24:00 web.1    |     at TraversalPath.call (/Users/jeff.winkler/repos/project/node_modules/babel-core/lib/babel/traversal/path/index.js:928:28)
11:24:00 web.1    |     at TraversalPath.visit (/Users/jeff.winkler/repos/project/node_modules/babel-core/lib/babel/traversal/path/index.js:978:14)
11:24:00 web.1    |     at TraversalContext.visitMultiple (/Users/jeff.winkler/repos/project/node_modules/babel-core/lib/babel/traversal/context.js:70:16)
11:24:00 web.1    |     at TraversalContext.visit (/Users/jeff.winkler/repos/project/node_modules/babel-core/lib/babel/traversal/context.js:100:19)
11:24:00 web.1    |     at Function.traverse.node (/Users/jeff.winkler/repos/project/node_modules/babel-core/lib/babel/traversal/index.js:64:17)
11:24:00 web.1    |     at TraversalPath.visit (/Users/jeff.winkler/repos/project/node_modules/babel-core/lib/babel/traversal/path/index.js:977:28)
11:24:00 web.1    |     at TraversalContext.visitSingle (/Users/jeff.winkler/repos/project/node_modules/babel-core/lib/babel/traversal/context.js:90:12)
11:24:00 web.1    |     at TraversalContext.visit (/Users/jeff.winkler/repos/project/node_modules/babel-core/lib/babel/traversal/context.js:102:19)
11:24:00 web.1    |  @ ./components/poc/CCPOCIndex.js 4:17-51
11:24:00 web.1    | 
11:24:00 web.1    | ERROR in ./components/editor/utils.js
11:24:00 web.1    | Module build failed: TypeError: /Users/jeff.winkler/repos/project/components/editor/utils.js: Object #<Scope> has no method 'generateUidBasedOnNode'
11:24:00 web.1    |     at createReferenceTo (/Users/jeff.winkler/repos/project/node_modules/babel-plugin-typecheck/lib/index.js:254:22)
11:24:00 web.1    |     at TraversalPath.exitNode (/Users/jeff.winkler/repos/project/node_modules/babel-plugin-typecheck/lib/index.js:307:17)
11:24:00 web.1    |     at TraversalPath.call (/Users/jeff.winkler/repos/project/node_modules/babel-core/lib/babel/traversal/path/index.js:928:28)
11:24:00 web.1    |     at TraversalPath.visit (/Users/jeff.winkler/repos/project/node_modules/babel-core/lib/babel/traversal/path/index.js:978:14)
11:24:00 web.1    |     at TraversalContext.visitMultiple (/Users/jeff.winkler/repos/project/node_modules/babel-core/lib/babel/traversal/context.js:70:16)
11:24:00 web.1    |     at TraversalContext.visit (/Users/jeff.winkler/repos/project/node_modules/babel-core/lib/babel/traversal/context.js:100:19)
11:24:00 web.1    |     at Function.traverse.node (/Users/jeff.winkler/repos/project/node_modules/babel-core/lib/babel/traversal/index.js:64:17)
11:24:00 web.1    |     at TraversalPath.visit (/Users/jeff.winkler/repos/project/node_modules/babel-core/lib/babel/traversal/path/index.js:977:28)
11:24:00 web.1    |     at TraversalContext.visitSingle (/Users/jeff.winkler/repos/project/node_modules/babel-core/lib/babel/traversal/context.js:90:12)
11:24:00 web.1    |     at TraversalContext.visit (/Users/jeff.winkler/repos/project/node_modules/babel-core/lib/babel/traversal/context.js:102:19)
11:24:00 web.1    |     at Function.traverse.node (/Users/jeff.winkler/repos/project/node_modules/babel-core/lib/babel/traversal/index.js:64:17)
11:24:00 web.1    |  @ ./components/editor/CCEditor.js 14:12-30
11:24:00 web.1    | webpack: bundle is now VALID.

Latest versions don't seem to help:

    "babel-core": "^5.4.7",
    "babel-eslint": "^3.1.7",
    "babel-loader": "^5.1.3",
    "babel-plugin-typecheck": "0.0.3",

Automatically typechecking imports

I'd love it if undefined imports would cause a build/runtime exception rather than causing a timebomb to find later on. Is this possible to implement?

import Foo, { bar} from './foo';

turns into

import Foo, { bar } from './foo';
if(typeof(Foo) === 'undefined') {
  throw new TypeError(`'Foo' default import from './foo' is undefined`);
}
if(typeof(bar) === 'undefined') {
  throw new TypeError(`'bar' import from './foo' is undefined`);
}

ES6 class support

I keep getting undefined and .kind TypeErrors when trying to Flow-annotate class methods like these:

class X {
  add (x: number, y: number): number { 
    return x + y; 
  }
}

Update v2

Please publish at least commit 10e6a04 to NPM, maybe together with some other applicable fixes from v3.

I haven't been able to test v3 much because Babel v6 is a mess and doesn't work for any of my projects. (It's currently quite user-hostile so I'm not sure how they're treating it as stable.)

Cannot read property 'name' of undefined

The following example:

class Bar
{

}

class Foo
{
    _bar: Bar;

    constructor()
    {
        this._bar = new Bar();
    }

    SetBar(): void
    {
        let bar: ?Bar = null;

        if (bar != null)
        {
            this._bar = bar;
        }
    }
}

Produces the error:

C:\Users\Scott\documents\github\test\node_modules\babel-core\lib\transformation\file\index.js:540
      throw err;
      ^

TypeError: C:/Users/Scott/documents/github/test/foo.js: Cannot read property 'name' of undefined
    at maybeInstanceOfAnnotation (C:\Users\Scott\documents\github\test\node_modules\babel-plugin-typecheck\lib\index.js:2377:44)
    at maybeInstanceOfAnnotation (C:\Users\Scott\documents\github\test\node_modules\babel-plugin-typecheck\lib\index.js:2375:16)
    at Object._instanceof [as instanceof] (C:\Users\Scott\documents\github\test\node_modules\babel-plugin-typecheck\lib\index.js:594:16)
    at staticCheckAnnotation (C:\Users\Scott\documents\github\test\node_modules\babel-plugin-typecheck\lib\index.js:1369:41)
    at AssignmentExpression (C:\Users\Scott\documents\github\test\node_modules\babel-plugin-typecheck\lib\index.js:404:16)
    at NodePath._call (C:\Users\Scott\documents\github\test\node_modules\babel-traverse\lib\path\context.js:74:18)
    at NodePath.call (C:\Users\Scott\documents\github\test\node_modules\babel-traverse\lib\path\context.js:46:17)
    at NodePath.visit (C:\Users\Scott\documents\github\test\node_modules\babel-traverse\lib\path\context.js:104:12)
    at TraversalContext.visitQueue (C:\Users\Scott\documents\github\test\node_modules\babel-traverse\lib\context.js:153:16)
    at TraversalContext.visitSingle (C:\Users\Scott\documents\github\test\node_modules\babel-traverse\lib\context.js:113:19)

When attempting to use the require hook or use the babel cli. Removing the _bar: Bar; removes the error.

importing & exporting types does not work as expected

When I use an imported type, the code uses an instanceof check instead of using the imported type checking function

Following the example code from #46, I put together a gist that shows the unexpected transform.

If I define the types within the same file, though, everything works great.

I'm probably doing something wrong, because importing and exporting works just fine in the babel-plugin-typecheck test suite. 😕

Does it perform static checks?

I've taken the first example from http://flowtype.org/docs/five-simple-examples.html and it compiles down to

/***/ function(module, exports) {

    'use strict';

    function foo(x) {
        return x * 10;
    }

    foo('Hello, world!');

/***/ },

without any error messages in webpack watch log.

Whereas the second example from that page compiles as

/***/ function(module, exports) {

    'use strict';

    function foo(x, y) {
        var _ref;

        if (typeof x !== 'string') throw new TypeError('Value of argument \'x\' violates contract, expected string got ' + (x === null ? 'null' : x instanceof Object && x.constructor ? x.constructor.name : typeof x));
        if (typeof y !== 'number') throw new TypeError('Value of argument \'y\' violates contract, expected number got ' + (y === null ? 'null' : y instanceof Object && y.constructor ? y.constructor.name : typeof y));
        _ref = x.length * y;
        if (typeof _ref !== 'string') throw new TypeError('Function \'foo\' return value violates contract, expected string got ' + (_ref === null ? 'null' : _ref instanceof Object && _ref.constructor ? _ref.constructor.name : typeof _ref));
        return _ref;
    }

    foo('Hello', 42);

/***/ },

which is expected.

Does it mean the plugin does not perform static checks?

Support object spread destructuring

With the following code:

function fn ({ a, ...b }: Object): Object {
  return { a, b }
}

It'll leave the object spread as-is, so when webpack processes it, it'll throw an error:
Unexpected token ....

Babel 6 Support

Love this plugin and I want to use it with Babel 6, but it does not appear to work. Any plans to support it?

v3.4.0 "Cannot iterate void"

I am running into a similar situation as in #68 where I receive an error trying to use the babel require hook, while I do not simply using babel-cli.

The following example:

class Foo
{
    constructor()
    {
        this.foo = [ ];
    }

    MyFunction(): void
    {
        for (let f: string of this.foo)
        {
            // ...
        }
    }
}

And the error is:

C:\Users\Scott\Documents\GitHub\test\node_modules\babel-core\lib\transformation\file\index.js:540
      throw err;
      ^

SyntaxError: C:/Users/Scott/Documents/GitHub/test/foo.js: Cannot iterate void
←[0m   8 |     MyFunction←[94m←[1m(←[22m←[39m←[94m←[1m)←[22m←[39m←[1m:←[22m ←[36mvoid←[39m
   9 |     ←[32m{←[39m
> 10 |         ←[36mfor←[39m ←[94m←[1m(←[22m←[39mlet f←[1m:←[22m string of ←[36mthis←[39m←[1m.←[22mfoo←[94m←[1m)←[22m←[39m
     |         ^
  11 |         ←[32m{←[39m
  12 |
  13 |         ←[32m}←[39m←[0m
    at File.buildCodeFrameError (C:\Users\Scott\Documents\GitHub\test\node_modules\babel-core\lib\transformation\file\index.js:407:15)
    at NodePath.buildCodeFrameError (C:\Users\Scott\Documents\GitHub\test\node_modules\babel-core\node_modules\babel-traverse\lib\path\index.js:148:26)
    at ForOfStatement (C:\Users\Scott\Documents\GitHub\test\node_modules\babel-plugin-typecheck\lib\index.js:434:20)
    at NodePath._call (C:\Users\Scott\Documents\GitHub\test\node_modules\babel-core\node_modules\babel-traverse\lib\path\context.js:74:18)
    at NodePath.call (C:\Users\Scott\Documents\GitHub\test\node_modules\babel-core\node_modules\babel-traverse\lib\path\context.js:46:17)
    at NodePath.visit (C:\Users\Scott\Documents\GitHub\test\node_modules\babel-core\node_modules\babel-traverse\lib\path\context.js:104:12)
    at TraversalContext.visitQueue (C:\Users\Scott\Documents\GitHub\test\node_modules\babel-core\node_modules\babel-traverse\lib\context.js:153:16)
    at TraversalContext.visitMultiple (C:\Users\Scott\Documents\GitHub\test\node_modules\babel-core\node_modules\babel-traverse\lib\context.js:108:17)
    at TraversalContext.visit (C:\Users\Scott\Documents\GitHub\test\node_modules\babel-core\node_modules\babel-traverse\lib\context.js:195:19)
    at Function.traverse.node (C:\Users\Scott\Documents\GitHub\test\node_modules\babel-core\node_modules\babel-traverse\lib\index.js:139:17)

Returning string literals

The v3.3.0 update generates an error on the following code when running node bar:

bar.js

require('babel-core/register')();
require('./foo.js');

foo.js

function foo(): string
{
    return '';
}
C:\Users\Scott\Documents\GitHub\test\node_modules\babel-core\lib\transformation\file\index.js:540
      throw err;
      ^

SyntaxError: C:/Users/Scott/Documents/GitHub/test/foo.js: Function "foo" did not return a value, expected string
←[0m> 1 | ←[36mfunction←[39m foo←[94m←[1m(←[22m←[39m←[94m←[1m)←[22m←[39m←[1m:←[22m string
    | ^
  2 | ←[32m{←[39m
  3 |     ←[36mreturn←[39m ←[31m''←[39m←[1m;←[22m
  4 | ←[32m}←[39m←[0m
    at File.buildCodeFrameError (C:\Users\Scott\Documents\GitHub\test\node_modules\babel-core\lib\transformation\file\index.js:407:15)
    at NodePath.buildCodeFrameError (C:\Users\Scott\Documents\GitHub\test\node_modules\babel-traverse\lib\path\index.js:148:26)
    at exit (C:\Users\Scott\Documents\GitHub\test\node_modules\babel-plugin-typecheck\lib\index.js:160:22)
    at NodePath._call (C:\Users\Scott\Documents\GitHub\test\node_modules\babel-traverse\lib\path\context.js:74:18)
    at NodePath.call (C:\Users\Scott\Documents\GitHub\test\node_modules\babel-traverse\lib\path\context.js:46:17)
    at NodePath.visit (C:\Users\Scott\Documents\GitHub\test\node_modules\babel-traverse\lib\path\context.js:116:8)
    at TraversalContext.visitQueue (C:\Users\Scott\Documents\GitHub\test\node_modules\babel-traverse\lib\context.js:153:16)
    at TraversalContext.visitMultiple (C:\Users\Scott\Documents\GitHub\test\node_modules\babel-traverse\lib\context.js:108:17)
    at TraversalContext.visit (C:\Users\Scott\Documents\GitHub\test\node_modules\babel-traverse\lib\context.js:195:19)
    at Function.traverse.node (C:\Users\Scott\Documents\GitHub\test\node_modules\babel-traverse\lib\index.js:139:17)

If I just do babel foo.js --out-dir dist, I can run the resulting file node foo without any errors.

Support for destructured variables?

Hi,

What are the thoughts/plans about supporting destructured variables, like this:

function({ state: string, id: number }) {
    // ...
}

Thanks

not check typed array on return value

Input:

function findBlocks(el: HTMLElement): Array<HTMLElement> {
    let blocks = [];

    if (el.hasAttribute('rt-is') || getComponentSubclass(el.tagName.toLowerCase())) {
        blocks.push(el);
    }

    blocks.push.apply(blocks, el.querySelectorAll(getComponentSelector()));

    return blocks;
}

Output:

function findBlocks(el) {
    if (!(el instanceof HTMLElement)) {
        throw new TypeError('Value of argument "el" violates contract, expected HTMLElement got ' + (el === null ? 'null' : (typeof el === 'undefined' ? 'undefined' : _typeof(el)) === 'object' && el.constructor ? el.constructor.name || '[Unknown Object]' : typeof el === 'undefined' ? 'undefined' : _typeof(el)));
    }

    function _ref(_id) {
        if (!(Array.isArray(_id) && _id.every(function (item) {
            return item instanceof HTMLElement;
        }))) {
            throw new TypeError('Function "findBlocks" return value violates contract, expected Array<HTMLElement> got ' + (_id === null ? 'null' : (typeof _id === 'undefined' ? 'undefined' : _typeof(_id)) === 'object' && _id.constructor ? _id.constructor.name || '[Unknown Object]' : typeof _id === 'undefined' ? 'undefined' : _typeof(_id)));
        }

        return _id;
    }

    var blocks = [];

    if (el.hasAttribute('rt-is') || getComponentSubclass(el.tagName.toLowerCase())) {
        blocks.push(el);
    }

    blocks.push.apply(blocks, el.querySelectorAll(getComponentSelector()));

    return blocks;
}

_ref is defined and not used.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.