0

TL;DR; my formatAPI function below is very much not the TYpeScript way of overriding a function...

I've got a function that might be called either of following four ways in JavaScript:

jsf.format("format", function(){ return ... }); // string, function

jsf.format("format"); // string

jsf.format({
  "format1": function(){ return ... },
  "format2": function(){ return ... }
}); // map[string:function]

jsf.format(); // no params

Each signature executes different logic beneath. CUrrently I've got the following:

function formatAPI(name: string, callback?: Function): any {
  if (callback) {
    registry.register(name, callback);
  } else if (typeof name === 'object') {
    registry.registerMany(name);
  } else if (name) {
    return registry.get(name);
  } else {
    return registry.list();
  }
}

but it's inconsistent - e.g. second parameter is string. When I change it to string|Object than it fails on my logic (registry.register(name, callback); - name here has to be a string and string|Object is not assignable to string). I'm trying to find a clear and extensible solution (to be able to add more variations if I need to, in the future), I try to adapt another question but I fail.

I would appreciate help and/or advise on how should I declare all possible signatures for this function AND the function body.

Community
  • 1
  • 1
ducin
  • 25,621
  • 41
  • 157
  • 256

1 Answers1

4

It slices! it dices! It makes toast!

// Signatures
function formatAPI(): string[];
function formatAPI(name: string): string;
function formatAPI(name: string, callback: (arg: string) => void): void;
function formatAPI(map: { [name: string]: () => string }): void;
// Implementation
function formatAPI(nameOrMap?: string | { [name: string]: () => string }, callback?:(arg: string) => void): string | string[] | void {
  if (typeof nameOrMap === 'string') {
    if (callback) {
      return registry.register(nameOrMap, callback);
    } else {
      return registry.get(nameOrMap);
    }
  } else if (...) {
    // Keep adding checks here
  }
}

One thing to note is that the compiler doesn't use the signatures to infer behavior for type guards. This means you should structure your code slightly more conservatively -- here, I put both string cases inside the typeof check.

Ryan Cavanaugh
  • 209,514
  • 56
  • 272
  • 235
  • great answer! So could you please explain me what does compiler use these signatures for, if it's not used for type guards? PS I can see that the TS compiler makes heavy use of ` if (typeof nameOrMap === 'string') {` :) thanks to `typeof` it knows the type context he's in. – ducin Apr 15 '16 at 16:13
  • The signatures define what it's legal to invoke the function with, and what's returned when you invoke with a specific set of types – Ryan Cavanaugh Apr 15 '16 at 16:18