TypL

The Type Linter for JS

About

TypL is still very much a work in progress. If you'd like to help, please get in touch!

Here's a quick example. In TypeScript/Flow, you might write code like this:

function isEven(v: number): boolean {
   return v % 2 == 0;
}

var x: number = 42;
var kind: boolean = isEven(x);

With TypL, you could instead write code like this:

function isEven(v = number) {
   return bool`${ v % 2 == 0 }`;
}

var x = number`42`;
var kind = isEven(x);

The = number annotation tells TypL's compile-time linter to expect a value of type 'number' for the v parameter of isEven(..). The return value of isEven(..) is annotated as type 'bool', though this annotation isn't necessary since the == equality operator always returns a value of type 'bool', so TypL can infer this.

The value 42 is annotated (aka, "tagged") as type 'number'. Again this isn't strictly necessary, but in this case TypL would have inferred a narrower type of 'int' if it hadn't been tagged. Since type 'int' is a subset of type 'number', it could still have been passed for the v parameter.

The kind variable is type-implied as type 'bool' because of the return type of isEven(..). If that return type couldn't be trusted, that line could have been:

var kind = bool`${ isEven(x) }`;

That annotation works as an assertion (runtime!) to ensure that the return value is in fact of type 'bool' before being assigned to kind. In either case, kind is type-implied as type 'bool', the difference being whether it's an explicit tagged-type or inferred.

Another difference from TypeScript/Flow is that TypL's linter will attempt to validate all checks at compile-time, and then remove any that it is able to validate, leaving only the annotations/assertions that must be checked at runtime. So, the above code would be compiled to this form for runtime:

function isEven(v) {
   // this line can be configured to be omitted!
   v = number`${ v }`;

   return v % 2 == 0;
}

var x = 42;
var kind = isEven(x);

The v = number`${ v }`; statement is inserted to validate at runtime that all inputs to isEven(..) are in fact numbers. However, if you don't want those assertions inserted, a configuration flag can be set to omit them.