#circular type
12 messages · Page 1 of 1 (latest)
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: {
...```
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?
Preview:```ts
type LtxInput = {
section: string
with?: LtxInput[]
// I'm using Record<string, unknown> because Record<string, any> also allows functions and arrays which I assume you don't want.
// See https://github.com/microsoft/TypeScript/wiki/Breaking-Changes/83af27fca396d172b4d895d480b10c3bacf89112#-k-string-unknown--is-no-longer-a-wildcard-assignment-targe
...```
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"]> : {}>
: {}
)