#circular type

12 messages · Page 1 of 1 (latest)

lyric blaze
#

I expected b to be included too, but there is only a. What am I doing wrong?

twin boneBOT
#
piscopancer#0

Preview:```ts
type _Ltx<With extends _Ltx[] = _Ltx<any>[]> = {
section: string
with?: With
entries?: Record<string, any> &
With[number]["entries"]
}

type Ltx<L extends _Ltx> = L extends L ? L : never

type Child = Ltx<{
section: "outfit_base"
with: [Parent]
entries: {
...```

faint crater
#

there's nothing circular here

#

why would you expect b to be included here exactly? Ltx isn't doing any transformations

#

are you trying to pull the entries from with recursively?

twin boneBOT
celest cobalt
#

I've written this in a bit of a weird way to avoid referring back to LtxInput

#

it makes CalculateEntries less DRY but it handled every test case I could throw at it

#

unlike more DRY types

#

The issue you were having was that there's no transformation of the input going on in Ltx; you write L extends L ? L : never which is basically the equivalent of if (x === x) { return x } else { return never } if this were js. Of course in general extends won't be able to modeled by such a simple if-else but in this specific case it's not doing you much.

#

Because _Ltx has a circular bound; _Ltx<With extends _Ltx[] = _Ltx<any>[]> you can get With[number]["entries"] to compile... but you're:
A) Missing the base case where With is an empty array/undefined. In the base case you probably get never or an internal type called errorType but no matter the effect you can see that you can easily write entries: 123 right now because the "no With case" is propagating up and making it so that entries can accept anything.
B) Even if you get this working you'd then require all calls of Ltx to already have the entries merged (because you tried defined _Ltx as already merged) which doesn't seem to be your intent. I'm assuming you want an input with unmerged entries and then the output to be it with merged entries.

#

Ah, there is a minor tweak depending on what you want for the array case:

type UnionToIntersection<U> = (U extends unknown ? (x: U) => void : never) extends (i: infer I) ? I : never

type CalculateEntries<
  Entries,
  With
> = 
  (Entries extends Record<string, unknown> ? Entries : never) &
  (
    With extends Array<infer L extends { entries: infer E, with?: infer W }>
      ? UnionToIntersection<L extends { entries: unknown, with?: unknown } ? CalculateEntries<L["entries"], L["with"]> : {}>
      : {}
  )