#Dependency resolution function with circular type references

15 messages · Page 1 of 1 (latest)

steady barn
#

I'm trying to construct a dependency resolution function (for a hobby project of mine, thylang.com), and I'm hitting a wall with implementing strong type safety for it.

I want to be able to provide an array of functions that all just get run but any given function in the array can obtain a dependency provided from any other function. I have no issues with implementing the runtime algorithm for this approach, but I can't come up with a good way to type it.

I'm coming to the conclusion that this problem is unsolvable for TypeScript, but I'm open to next-level black-magic solutions that defy TypeScript limits (e.g. https://github.com/Microsoft/TypeScript/issues/3496#issuecomment-128553540) or to alternative solutions which mostly meet the requirements of my system while adding additional usage burden.

(Playground link in next message)

merry riverBOT
#
sunshine_sower#0

Preview:```ts
function makeThy<
BlocksTuple extends readonly ((args: {
// Ideally this type would probably be stricter, but I haven't had success either way.
thy: (key: string) => unknown
}) => Record<string, unknown>)[]

(blocks: BlocksTuple) {
// Make a union of types returned by all of the blocks.
type ReturnTypes = {
...```

limpid niche
#

If you are willing to concede a little bit of duplication in the form of:

const thy = makeThy(blocksActual)
type Thy = ThyOf<typeof blocksActual>

Here's a solution:

merry riverBOT
#
nonspicyburrito#0

Preview:```ts
type U2I<U> = (
U extends U ? (u: U) => 0 : never
) extends (i: infer I) => 0
? Extract<I, U>
: never

type Blocks = readonly ((args: {
thy: (key: PropertyKey) => never
}) => object)[]

type BlockResult<T extends Blocks> = U2I<
{
[K in keyof T]: ReturnType<T[K
...```

steady barn
#

Hmm, thanks for the input. I'm not quite seeing what makes the difference, but it does still seem to fall apart if I remove the explicit type when calling thy('a'). I only had that in there to demonstrate the type safety, but I don't want it there in the real use case.

merry riverBOT
#
sunshine_sower#0

Preview:ts ... // const a: 'A' = thy('a') const a = thy("a") return {b: `${a}B` as const} ...

limpid niche
#

Eh, probably can get it to work with a different implementation of ThyOf.

#

Hmm maybe not.

steady barn
#

Here's a slightly different (somewhat simpler?) approach. It still doesn't work though.

merry riverBOT
#
sunshine_sower#0

Preview:```ts
type BaseBlockReturns = readonly object[]
type ThyOfInner<
T extends object,
K extends string

= T extends unknown
? K extends keyof T
? T[K]
: never
: never
type ThyOf<T extends BaseBlockReturns> = <
K extends string
(
key: K
) => ThyOfInner<T[number], K>

type BlockReturn
...```

steady barn
#

Might have something on the right track...

merry riverBOT
#
sunshine_sower#0

Preview:```ts
type BaseFunction = (...args: any) => any
type BaseBlockInfo = {
keys: string
provider: BaseFunction
}
type BaseBlockInfos = readonly BaseBlockInfo[]
type ThyOfInner<
BlockInfo extends BaseBlockInfo,
K extends string

= BlockInfo extends unknown
? K extends keyof ReturnType<BlockInfo["provider"]>
? ReturnType<BlockInfo["provider"]>[K]
: never
: never
...```

steady barn
#

^^ That definitely doesn't work if I scale out further though.

steady barn
#

!helper

steady barn
#

I found a solution that I think is serviceable for my purposes, though not as ideal as I hoped.