Pregunta

I'm new to TypeScript and trying to use it with AngularJS. Here's a situation that's bothering me because I can't get an error from the compiler.

In Angular (AFAIK), a resource provider has a get() method that returns an instance of T, T being a type parameter. This method has many other overloads, and can accept params or data parameters, and possibly a success and an error handler with signatures like this:

interface IResourceSuccessCallbackFunction<T> {
    (value: T, responseHeaders: Array<string>): void;
}

interface IResourceErrorCallbackFunction {
    (httpResponse: string): void;
}

I took inspiration from the corresponding declaration file at DefinitelyTyped to come up with these overloads for the get function:

interface IResourceClass<T> {
    get(): T;
    get(dataOrParams: any): T;
    get(dataOrParams: any, success: IResourceSuccessCallbackFunction<T>): T;
    get(success: IResourceSuccessCallbackFunction<T>, error?: IResourceErrorCallbackFunction): T;
    get(params: any, data: any, success?: IResourceSuccessCallbackFunction<T>, error?: IResourceErrorCallbackFunction): T;
}

My problem is that those definitions allow me to write stuff like this:

var resourceService: IResourceClass<number> = null;
var res1 = resourceService.get({ id: 1000 }, function() {
    console.log("fail");
});
var res2 = resourceService.get(function(p: Phone) {
    console.log(p);
}, function() {
    console.log("fail");
});

Obviously, here, for res1, the programmer made a mistake by passing an error handler as second argument, forgetting about the success handler; and for res2, the supposed success handler is accepting the wrong type Phone instead of number.

Now, I know why those two calls compile and are valid: the last overloaded version is always a match, because it allows two any params and two optional params, which are here absent. So I'm looking for a way to define the overloaded variants that would catch those errors. For instance, replace any with a type that would say “not a function”, or though some other trick.

Is this possible?

¿Fue útil?

Solución

If possible, remove the overloads with any. I'm not sure if that's applicable in this case; it depends on how the API works.

res1 will always be OK because you're not required to consume parameters in TypeScript (imagine how annoying it would be if you had to have a three-parameter callback to Array#forEach).

For res2, you can sometimes improve the situation by adding "trap" overloads:

interface IResourceClass<T> {
    get(): T;
    get(success: IResourceSuccessCallbackFunction<T>, error?: IResourceErrorCallbackFunction): T;
    get(otherFunction1: (...a: any[]) => void, otherFunction2: (...a: any[]) => void): { YOU_HAVE_FAILED: {}; };
    get(dataOrParams: any): T;
    get(dataOrParams: any, success: IResourceSuccessCallbackFunction<T>): T;
    get(params: any, data: any, success?: IResourceSuccessCallbackFunction<T>, error?: IResourceErrorCallbackFunction): T;
}

var resourceService: IResourceClass<number> = null;
var res1 = resourceService.get({ id: 1000 }, function() {
    console.log("fail");
});
// res2: { YOU_HAVE_FAILED: {} }
var res2 = resourceService.get(function(p: Phone) {
    console.log(p);
}, function() {
    console.log("fail");
});

This won't be a compile error immediately, but any practical use of res2 will be an error.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top