admin管理员组

文章数量:1296894

I’m trying to create a TypeScript utility type (LeafValues) that extracts the deeply nested values (leaves -- the values at the deepest level that aren’t objects themselves) from an object while preserving their literal types (e.g., "hello" instead of string). However, TypeScript defaults to widening these values to their primitive types (e.g., string), unless I use as const on the object. I want to avoid adding as const to every object.

Here’s My LeafValues Utility Type:

type LeafValues<T extends object> = {
  [K in keyof T]: T[K] extends object ? LeafValues<T[K]> : T[K];
}[keyof T];

This type recursively traverses an object and extracts the values at the deepest level (the "leaves"), which are not objects themselves.

Here’s an example object:

const obj = {
  foo: "hello",
  bar: {
    baz: "world",
  },
};

When I use LeafValues with this object:

type Result = LeafValues<typeof obj>;

Expected Result:

I expect Result to be the union of literal types: "hello" | "world".

Actual Result:

Instead, TypeScript infers Result as string.

I think This Happens because TypeScript widens the literal types ("hello" and "world") to their primitive type (string) unless the object is marked as as const.

What I Tried:

  1. Using as const works, but I don’t want to modify every object:

    const obj = { foo: "hello", bar: { baz: "world" } } as const // <--;
    type Result = LeafValues<typeof obj>; // "hello" | "world" ✅
    

What I Want:

I want LeafValues to infer literal types "hello" | "world" without requiring as const on the object.

My question:

Is there a way to infer literal types from deeply nested objects without using as const? If not, are there any workarounds or alternative approaches?

本文标签: typescriptHow to Infer Literal Types from Deeply Nested Objects Without Using as constStack Overflow