2

I want to build an API, inspired by jest it function, which will look like this:

Typescript

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

So I'll be able to use it like this:

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

If I was building it in pure JS, I'd make a few different functions, and then give them properties that would point towards one another:

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

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

But in TS I can't do this, as an object has to have all required properties when it's created, so I'm forced to use casts, but: (1) it is ugly, and (2) I feel like there's a much easier pattern to construct this.

So, is there a standard and type safe pattern (or library) which is commonly used to construct objects like these in Typescript? Maybe there are existing open source projects written in Typescript which can serve as example for such task?


This question is building on question about function properties — it's not duplicate, I need to do more things which are more specific.

Max Yankov
  • 12,551
  • 12
  • 67
  • 135
  • *"I need to do more things which are more specific"* - what things? Can you give an example that _wouldn't_ work with the proposed solutions? – jonrsharpe May 18 '21 at 17:00
  • @jonrsharpe I think I already did. The question I link to just adds properties, which are not self-referential. – Max Yankov May 18 '21 at 17:12
  • 1
    I don't see why that means you can't use the same pattern of e.g. an IIFE where the return value is fully constructed. *"I'm forced to use casts"* - yes, because what you create initially _isn't a `Something`_. – jonrsharpe May 18 '21 at 17:15
  • @jonrsharpe my question specifically asks if there's a less ugly way. May there really isn't, which would also be an answer to my question. I can use casts, yes; but I'm used to cast things in typed languages only after making sure that no other solution exists. – Max Yankov May 18 '21 at 18:07
  • What would "less ugly" be? That seems quite opinion-based. And it's a little disingenuous to claim your needs are more specific in some way when you just don't like it. – jonrsharpe May 18 '21 at 18:29
  • In this context, "less ugly" would be "without casts", because in my opinion (completely agree about it being opinion based) every cast is a promise that you make to a type system that you now responsible for and have to keep, and I try to keep amount of promises I make in my code to a minimum. – Max Yankov May 18 '21 at 18:40

1 Answers1

1

Well, i guess you can achive that with some boilerplate assignments:

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)

aleksxor
  • 7,535
  • 1
  • 22
  • 27
  • I didn't know that Typescript compiler actually changes type of `something` with each line that assigns it a property, and yet I somehow considered myself a pretty advanced Typescript user. This is awesome. Thanks a lot for your answer! – Max Yankov May 23 '21 at 18:16