Repository navigation
Description
🔎 Search Terms
TS5115, TS2589, "appear infinitely circular", "excessively deep and possibly infinite", variance, closure, 4820
🕗 Version & Regression Information
- This changed between versions
7.1.0-dev.20260805.1and7.1.0-dev.20260806.1. The only commit between them is Order variance computation by associated type symbol typescript-go#4820 "Order variance computation by associated type symbol". 7.1.0-dev.20260806.1reports TS2589. Since Restore idempotency toresolveObjectTypeMembers#64372 (7.1.0-dev.20260929.1), the same error is reported as TS5115.7.0.2and7.1.0-dev.20260805.1report nothing. Still present in7.1.0-betaand7.1.0-dev.20261111.1.- This is not the case of TS5115 in published Zod 4.5–4.6 types after #64372 #64605: there is no base type argument that refers back to its own type, and the error starts before Restore idempotency to
resolveObjectTypeMembers#64372.
⏯ Playground Link
Repository with every version pinned: https://github.com/RomainLanz/tsrs-type-issue (npm install && npm run check:all)
💻 Code
The types are reduced from @adonisjs/http-transformers 2.3.1, with no dependencies. strict, target: esnext.
type Prettify<T> = { [K in keyof T]: T[K] } & {};
type ExtractUndefined<T> = { [K in keyof T]: undefined extends T[K] ? K : never }[keyof T];
type ExtractDefined<T> = { [K in keyof T]: undefined extends T[K] ? never : K }[keyof T];
type Next = [1, 2, 3, 4, 5, 6];
type JSONPrimitives = string | number | bigint | boolean | null | undefined;
type JSONValues = JSONPrimitives | CanBeSerialized<any>;
type CanBeSerialized<T extends JSONDataTypes> = {
toJSON(): T;
};
type JSONDataTypes = JSONValues | JSONDataTypes[] | {
[key: string]: JSONDataTypes;
};
type SerializeJSONObject<T> = {
[K in ExtractDefined<T>]: SerializeJSONTypes<T[K]>;
} & {
[K in ExtractUndefined<T>]?: SerializeJSONTypes<T[K]>;
};
interface ExtendedJSONTypes {
}
type NonAllowedJSTypes = Map<any, any> | Set<any>;
type ForcefullyAllowedTypes = {
[K in keyof ExtendedJSONTypes]: ExtendedJSONTypes[K];
}[keyof ExtendedJSONTypes];
type SerializeJSONOwnTypes<T> = T extends JSONPrimitives ? T : T extends ForcefullyAllowedTypes ? T : T extends NonAllowedJSTypes ? never : T extends Array<infer item> ? Array<SerializeJSONTypes<item>> : T extends object ? Prettify<SerializeJSONObject<T>> : never;
type SerializeJSONTypes<T> = T extends ForcefullyAllowedTypes ? T : T extends CanBeSerialized<infer A> ? SerializeJSONOwnTypes<A> : SerializeJSONOwnTypes<T>;
interface ItemContract<Transformer extends Record<string, any>, MaxDepth extends Next[number], Variant extends string> {
$type: 'item';
transformer: {
new (...args: any[]): Transformer;
};
maxDepth: MaxDepth;
variant: Variant;
}
type UnpackKeyValue<Value extends [any, any], MaxDepth extends number, Depth extends number> = [Value[0]] extends [never] ? SerializeJSONTypes<Value[1]> : Value[0] extends ItemContract<infer Transformer, infer LocalMaxDepth, infer Variant> ? InferData<Transformer, Variant, MaxDepth extends -1 ? LocalMaxDepth : MaxDepth, Next[Depth]> | SerializeJSONTypes<Value[1]> : SerializeJSONTypes<Value[1]>;
type LimitDepth<Key, Value, MaxDepth extends number, Depth extends number> = [
Value
] extends [never] ? Key : Value extends ItemContract<any, any, any> ? MaxDepth extends Depth ? never : Key : Key;
type UnpackOptionalValues<Data, MaxDepth extends number, Depth extends number> = {
[O in {
[K in keyof Data]: [undefined] extends [Data[K]] ? LimitDepth<K, SplitItm<Data[K]>[0], MaxDepth, Depth> : never;
}[keyof Data]]?: UnpackKeyValue<SplitItm<Data[O]>, MaxDepth, Depth>;
};
type UnpackRequiredValues<Data, MaxDepth extends number, Depth extends number> = {
[O in {
[K in keyof Data]: [undefined] extends [Data[K]] ? never : LimitDepth<K, SplitItm<Data[K]>[0], MaxDepth, Depth>;
}[keyof Data]]: UnpackKeyValue<SplitItm<Data[O]>, MaxDepth, Depth>;
};
type SplitItm<T> = [
Extract<T, ItemContract<any, any, any>>,
Exclude<T, ItemContract<any, any, any>>
];
type UnpackAsTopLevelItem<T, Wrapper extends string | undefined> = T extends ItemContract<infer Transformer, any, infer Variant> ? InferData<Transformer, Variant, -1, 0> : never;
type UnpackValues<Data, MaxDepth extends number, Depth extends number> = Prettify<UnpackRequiredValues<Data, MaxDepth, Depth> & UnpackOptionalValues<Data, MaxDepth, Depth>>;
type InferData<Transformer, Variant extends string = 'toObject', MaxDepth extends number = -1, Depth extends number = 0> = Transformer extends {
[K in Variant]: (...args: any[]) => unknown;
} ? UnpackValues<Awaited<ReturnType<Transformer[Variant]>>, MaxDepth, Depth> : never;
declare class Item<Transformer extends Record<string, any>, MaxDepth extends Next[number], Variant extends string> {
transformer: { new (...args: any[]): Transformer };
maxDepth: MaxDepth;
variant: Variant;
$type: 'item';
resolve(): Promise<UnpackAsTopLevelItem<this, undefined>>;
}
type Ctor = { new (resource: any, ...rest: any[]): any };
declare abstract class BaseTransformer<T> {
static transform<Self extends Ctor>(this: Self, data: ConstructorParameters<Self>[0]): Item<InstanceType<Self>, 1, 'toObject'>;
constructor(resource: T);
}
class Size {
}
class SizeTransformer extends BaseTransformer<Size> {
toObject() {
}
}
declare function once<T>(value: T): T;
once(() => SizeTransformer.transform(new Size()));🙁 Actual behavior
repro.ts(76,12): error TS5115: Instantiations of the following types appear infinitely circular: 'InferData', 'UnpackKeyValue'.With 7.1.0-dev.20260806.1 to 7.1.0-dev.20260928.1:
repro.ts(76,12): error TS2589: Type instantiation is excessively deep and possibly infinite.🙂 Expected behavior
No error, as in 7.0.2 and 7.1.0-dev.20260805.1. The recursion is bounded: each UnpackKeyValue → InferData step advances Depth through Next = [1, 2, 3, 4, 5, 6]. toObject() here returns void, so there is not even a property to recurse into.
Additional information about the issue
The error depends on how the value reaches the generic call. Passing the Item directly checks without error on every version:
once(() => SizeTransformer.transform(new Size())); // TS5115
once(SizeTransformer.transform(new Size())); // no errorMy guess, not verified: microsoft/typescript-go#4820 restarts variance computations from the earliest declared symbol when it detects a circularity, and the inference of the closure's return type may now reach that restart through InferData and UnpackKeyValue. We found it on a real AdonisJS application; tsrs, which ports 7.1, reports the same error.
Activity