Recursive type definition results in “Type instantiation is excessively deep and possibly infinite (ts 2589)” TypeScript error when using type

26 views Asked by At

I’m encountering the TypeScript error Type instantiation is excessively deep and possibly infinite (ts 2589) when using a recursive type definition on a typed const. Here’s a simplified version of my type code:

// Type aliases
export type Version = number;
export type Shape = object;
export type ShapeRecord = Record<Version, Shape>;

// Recursive Max
type Max<N extends number, A extends unknown[] = []> =
  [N] extends [Partial<A>["length"]]
  ? A["length"] & N
  : Max<N, [0, ...A]>;

// Type helpers
type ShapeVersion<TShapeRecord extends ShapeRecord> = keyof TShapeRecord & Version;
type LatestShapeVersion<TShapeRecord extends ShapeRecord> = Max<ShapeVersion<TShapeRecord>>;

// Helper function
const getLatestVersion = <TShapeRecord extends ShapeRecord>(shapeRecord: TShapeRecord): LatestShapeVersion<TShapeRecord> =>
  Math.max(...Object.keys(shapeRecord).map(Number)) as LatestShapeVersion<TShapeRecord>;

(Recursive Max taken from this answer)

When I try to use the getLatestVersion function on an untyped object, everything works well:

const untypedShapeRecord = {
  5: {},
  4: {},
  3: {},
  2: {},
  1: {},
};
const latestVersion = getLatestVersion(untypedShapeRecord);
//    ^? const latestVersion: 5

However, when I add the ShapeRecord type to my const, TypeScript suddenly complains that the type instantiation is excessively deep and possibly infinite:

const typedShapeRecord: ShapeRecord = {
  5: {},
  4: {},
  3: {},
  2: {},
  1: {},
};
const notLatestVersion = getLatestVersion(typedShapeRecord); // Error: Type instantiation is excessively deep and possibly infinite (ts 2589)
//    ^? const notLatestVersion: any

The version numbers are currently not higher than 6, so the actual level of recursion should be that high. This number is not expect to ever get much higher than 20 - two digits at most. I've tested the Max type with higher values, and it seems to die when values are higher or equal to 1000.

How can I modify my types to avoid this error? Is there a better way to achieve the same functionality without running into this issue?

Playground link

0

There are 0 answers