Πώς μπορώ να δημιουργήσω ένα API ιδιοτήτων συναρτήσεων που αναφέρονται στον εαυτό τους σε Typescript;


Θέλω να φτιάξω ένα API, εμπνευσμένο από τη λειτουργία it του jest, το οποίο θα φαίνεται έτσι:

Typescript

interface Something {
    (param: number): void
    modifier: Something
    anotherModifier: Something
}

Έτσι θα μπορώ να το χρησιμοποιώ έτσι:

s()
s.modifier()
s.anotherModifier.modifier()

Αν το κατασκεύαζα σε καθαρό JS, θα έφτιαχνα μερικές διαφορετικές συναρτήσεις, και στη συνέχεια θα τους έδινα ιδιότητες που θα δείχνουν η μία προς την άλλη:

const pure = (a) => {}
const modifier = (a) => {}
const anotherModifier = (a) => {}

for(const handler of [pure, modifier, anotherModifier]) {
    handler.modifier = modifier
    handler.anotherModifier = anotherModifier
}

Αλλά στο TS δεν μπορώ να το κάνω αυτό, καθώς ένα αντικείμενο πρέπει να έχει όλες τις απαιτούμενες ιδιότητες όταν δημιουργείται, οπότε αναγκάζομαι να χρησιμοποιώ μετατροπές τύπων, αλλά: (1) είναι άσχημο, και (2) νιώθω ότι υπάρχει ένα πολύ πιο εύκολο πρότυπο για να το κατασκευάσω.

Λοιπόν, υπάρχει κάποιο πρότυπο και τύπου ασφαλές μοτίβο (ή βιβλιοθήκη) που είναι συνήθως χρησιμοποιούμενο για τη δημιουργία αντικειμένων όπως αυτά στο Typescript; Ίσως να υπάρχουν υπάρχοντα έργα ανοιχτού κώδικα γραμμένα σε Typescript που μπορούν να χρησιμεύσουν ως παράδειγμα για μια τέτοια εργασία;


Αυτή η ερώτηση βασίζεται στην ερώτηση σχετικά με τις ιδιότητες των συναρτήσεων — δεν είναι διπλότυπη, χρειάζεται να κάνω περισσότερα πράγματα που είναι πιο συγκεκριμένα.

Απάντηση #1

Λοιπόν, φαντάζομαι ότι μπορείς να το πετύχεις με μερικές προκαθορισμένες αναθέσεις:

interface Something {
    (param: number): void
    modifier: Something
    anotherModifier: Something
}

function getSomething(): Something {
    function anotherModifier(a: number) {}
    function something(a: number) {}
    function modifier(n: number) {}

    something.modifier = modifier
    something.anotherModifier = anotherModifier

    modifier.modifier = modifier
    modifier.anotherModifier = anotherModifier

    anotherModifier.modifier = modifier
    anotherModifier.anotherModifier = anotherModifier

    return something
}

const s = getSomething()

s(1)
s.modifier(1)
s.anotherModifier.modifier(1)