How to create the analogue of Java functional interface default method in TypeScript?

Pavel_K :

There is a project that implements Java(FX) API in TypeScript/JavaScript (Script4J). And now I want to modify Comparator functional interface. This is default solution:

export interface Comparator<T> {
    (o1: T, o2: T): number;
}

Such solution allows to add comparators as arrow functions (like java lambda), for example:

let comparator: Comparator<number> = (n1: number, n2: number): number => {
    return n1 - n2;
};
let treeMap: SortedMap<number, string> = new TreeMap<number, string>(comparator);

As you see the code is very clean. Now I need to add to TypeScript Comparator interface next method (Java code):

default Comparator<T> reversed() {
    return Collections.reverseOrder(this);
}

What is the best way to do it, taking into consideration that I can't change API?

Pavel_K :

After some thinking I decided the only way to reach maximum Java API is to use abstract classes instead of interfaces. This is my solution:

function applyMixins(derivedCtor: any, baseCtors: any[]) {
    baseCtors.forEach(baseCtor => {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
             if (name !== 'constructor') {
                derivedCtor.prototype[name] = baseCtor.prototype[name];
            }
        });
    }); 
}

type ComparatorFunc<T> = (o1: T, o2: T) => any;

abstract class Comparator<T> {

    public static lambda<T>(func: ComparatorFunc<T>): Comparator<T> {
        return new class extends Comparator<T> {
            public compare(o1: T, o2: T): number {
                return func(o1, o2);
            }
        }
    }

    public abstract compare(o1: T, o2: T): number;
}

//VAR 1 - lambda
let lambdaComparator: Comparator<number> = Comparator.lambda((n1: number, n2: number) => { return n1 - n2;});
console.log("Lambda comparator");
console.log(lambdaComparator.compare(100, 50));

//VAR 2 - full implementation
class MyComparator implements Comparator<number> {

    public compare(o1: number, o2: number): number {
        return o1 - o2;
    }
}
applyMixins (MyComparator, [Comparator]);

let classComparator: MyComparator = new MyComparator();
console.log("Class comparator");
console.log(classComparator.compare(100, 50));

Advantages:

  • We are very close to Java API.
  • The names of the methods are visible.
  • We can use arrow functions to quickly create instance of class.
  • We can create class that implements multiple interfaces.
  • As all classes that implement interfaces pass through one function (applyMixins) we can add support of the methods that can check if certain instance implements certain interface (class easily checked via instanceof).

Disadvantages:

  • Call applyMixins for every class every time program starts/page opens.
  • It is necessary to show empty default methods (reversed:()=> Comparator) in every implementation as TypeScript checks their implementation.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=157909&siteId=1