1

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.

1 Answer 1

5

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.

Sign up to request clarification or add additional context in comments.

2 Comments

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.
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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.