AngularJs TypeScript typings and --strictFunctionTypes #16617
Description
I'm submitting a ...
- bug report
- feature request
- design discussion
TypeScript introduced a new strictness flag with TS 2.7 - https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#strict-function-types
It makes typechecking for callback based APIs much stricter. For example:
function f(mixedArray: (string|number)[]) {
mixedArray.map((x: string) => x.charAt(0));
}
only errors when --strictFunctionTypes is turned on.
AngularJs has many callback based APIs, which will be checked at more strictly with the flag. Some real bugs will be exposed, but also it will start erring on many correct patterns. For example:
{
require: 'ngModel'
link: (...., controller: NgModelController) {...}
This is now an error because TS doesn't know that require and link are related. As written the typings require one handle the full spectrum of options in the link function IController | IController[] | {[key: string]: IController}
.
TS types are quite expressive and @gkalpak and I tried to write up some prototype of typings that are aware of the link between require and link:
interface IDirective<T extends string|string[]> {
link?: (x: LinkT<T>) => void;
require?: T;
}
type LinkT<T> =
T extends string ? IController : T extends string[] ? IController[] : never;
But even with full conditional types, it is impossible to extend this for user-defined directives.
So as plan B, I am going to contribute some nice type aliases to DefinitelyTyped type LinkFnControllerType = IController | IController[] | {[key: string]: IController}
and propose the canonical solution to be writing the link function as:
link: (..., controller: angular.LinkFnControllerType) => {
let myController = controller as MyControllerType;
}
One added benefit is that there is an explicit 'as' cast to remind the reader to check with the 'require' and controller statements.