Guide on extracting a data type from a nested attribute by utilizing a "path tuple"

After discovering this revolutionary piece of code in the
Typescript: deep keyof of a nested object link,

type Cons<H, T> = T extends readonly any[] ?
    ((h: H, ...t: T) => void) extends ((...r: infer R) => void) ? R : never
    : never;

type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
    11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...0[]]

type Paths<T, D extends number = 10> = [D] extends [never] ? never : T extends object ?
    { [K in keyof T]-?: [K] | (Paths<T[K], Prev[D]> extends infer P ?
        P extends [] ? never : Cons<K, P> : never
    ) }[keyof T]
    : [];

This incredible code snippet helps us extract the nested paths of an object as union tuples, like so:

type Obj = {
  A: { a1: string }
  B: { b1: string, b2: { b2a: string } }
}

type ObjPaths = Paths<Obj> // ['A'] | ['A', 'a1'] | ['B'] | ['B', 'b1'] | ['B', 'b2'] | ['B', 'b2', 'b2a']

I am on a quest to find a way to retrieve a type from a nested property using a path tuple, structured as follows:

type TypeAtPath<T extends object, U extends Paths<T>> = ...

The issue I am facing is that the compiler shows me the error message:

Type instantiation is excessively deep and possibly infinite
.

Fortunately, I stumbled upon a solution to eliminate this error by specifying T more precisely:

type TypeAtPath<T extends {[key: string]: any}, U extends Paths<T>> = T[U[0]]

However, this method only functions for top-level paths, and I fear my TypeScript skills may not be sufficient for this challenge.

Answer №1

UPDATE FOR TS 4.1

With the recent enhancements in TypeScript such as recursive conditional types and variadic tuple types, creating a simpler version of the DeepIndex type is now possible:

type DeepIndex<T, KS extends Keys, Fail = undefined> =
    KS extends [infer F, ...infer R] ? F extends keyof T ? R extends Keys ?
    DeepIndex<T[F], R, Fail> : Fail : Fail : T;

While this may still exhibit some quirks on tree-like structures, the advancements have made it more manageable compared to earlier versions.

To achieve a similar deep-indexing type without unsupported recursion, an alternative approach can be taken by unrolling the intended recursive type into non-recursive types that terminate at certain depth limits:

type Tail<T> = T extends readonly any[] ?
    ((...t: T) => void) extends ((h: any, ...r: infer R) => void) ? R : never
    : never;

The implementation of DeepIndex follows a series of nested index checks while walking down nested properties which should ultimately produce the desired output or a specified failure type if necessary.


A sample usage of the deepIndex() function demonstrates its functionality across nested property access scenarios:

const obj = {
    a: { b: { c: 1 }, d: { e: "" } },
    f: { g: { h: { i: true } } }, j: { k: [{ l: "hey" }] }
}

const c = deepIndex(obj, "a", "b", "c"); // number 
const e = deepIndex(obj, "a", "d", "e"); // string
const i = deepIndex(obj, "f", "g", "h", "i"); // boolean
const l = deepIndex(obj, "j", "k", 0, "l"); // string
const oops = deepIndex(obj, "a", "b", "c", "d"); // undefined
const hmm = deepIndex(obj, "a", "b", "c", "toFixed"); // (fractionDigits?: number) => string

It's important to note that enhancing the type checking capabilities of deepIndex() beyond outputting undefined for invalid paths may prove challenging due to limitations and potential issues with the compiler. Further exploration in this area might yield solutions with improved error handling and constraints.

For now, these workarounds provide functional alternatives but pushing the boundaries too far could lead to complexity and unpredictable results.


Hopefully this sheds some light on advanced type manipulation in TypeScript; best of luck with your endeavors!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Guide to logging data from a response using the console

I have a function that retrieves data from an API: return this._http.get(`api/data`) .map((response: Response) => response.json()); What is the best way to debug or inspect the response, besides using console.log(response.json())? ...

What are the pros and cons of employing a switch case within the useEffect function in ReactJS?

I am currently managing various functionalities through multiple functions that handle different states. These functions are triggered when specific states are updated. With numerous states to update, I have implemented a method where I create individual f ...

Using Typescript to implement a conditional return type and ensuring that the value types are consistent together

I am working with a useSelectedToggle hook that helps in connecting the UI state to the open/closed status of a dialog where it will be displayed. The toggle defines the value as (T) when it is open, and null when it is closed. How can I enforce stricter ...

What is the most effective way to retrieve a specific type of sibling property in TypeScript?

Consider the code snippet below: const useExample = (options: { Component: React.ComponentType props: React.ComponentProps<typeof options.Component> }) => { return } const Foo = (props: {bar: string; baz: number}) => <></& ...

How can I ensure that a user variable stored in an Angular6 service remains defined and accessible from other components?

Currently, I am working on an Angular app and facing a challenge. After receiving a user variable from an asynchronous call to Firestore Cloud, I noticed that the variable is successfully set (verified using console.log()). However, when I navigate between ...

What is the best way to generate a unique UUID for every object created within a loop?

I am working on a typescript method that eliminates hashtags from a string and saves them in an array within a model. Each element in the hashtag array is assigned a unique UUID along with the complete string added to the model. However, I am facing an iss ...

Can a function be annotated in order to inform the TypeScript compiler that it has verified the type of a class property?

How can I annotate `readText` in the code snippet below to assure the compiler that `this.text` is of type `string` and not `string | undefined`? type MyResponse = { text: () => Promise<string>; }; class ResponseVerfier { response: MyRespons ...

Display a message if the app is unable to retrieve the current position within X seconds

In my current project using ionic3 and angular 4, there are times when retrieving the user's current location can be challenging due to slow internet connection. To address this issue, I would like to implement a feature where if after 30 seconds the ...

Is the scrolling functionality acting strange while using React Three Fiber?

In my React Three Fiber application, I have the following structure: Website Canvas NativeHTMLContent Canvas Website The issue I'm facing is that after scrolling down the entire canvas, the scrollbar resets to the top and starts scrolling from the t ...

Instructing one class to delegate its redirect functions to another class

Within my JavaScript code, I have a class called class1 that takes in another class called class2 as a parameter in the constructor. My goal is to be able to access all the functions of class2 directly from class1, without having to manually declare each ...

React Redux encountering issues with state updates

Currently, I am utilizing the React Hooks version of React-Redux. Below is an overview of my code structure. Strangely, any values that I include in my dispatch(function(value)) seem to not be recognized within my reducer. I am perplexed by this issue. Th ...

Encountering an issue when trying to upload a photo from Angular 8 to Laravel: receiving a "Call to a member function extension() on null" error

In my project using Angular 8 for the front end and Laravel 5.8 for the backend, I encountered an issue with uploading photos. I found guidance in this tutorial from ACADE MIND. Here is my template code : <input *ngIf="photoEdit" enctype="multipart/ ...

Develop a structured type that encompasses the stationary attributes of an object-oriented class

Provided are the following classes: class EnumerationDTO { designation: string; id: number; } class ExecutionStatusDTO extends EnumerationDTO { static readonly open: ExecutionStatusDTO = { id: 0, designation: 'Open' }; static readonl ...

What is the proper way to utilize a function in C# that integrates with a window form using TypeScript?

I am currently working on a code that is in c# and utilizes a web browser. My goal is to convert the existing JavaScript code to Angular 7 and Typescript. Below is the c# code and the corresponding JavaScript code used to access the c# function from JavaS ...

Using js/jsx files in a TypeScript + Webpack project: A beginner's guide

I encountered an issue while trying to import and utilize a regular .jsx file within my typescript project that is built with webpack. The error message I received reads: ERROR in ./src/components/App/Test.jsx 72:4 Module parse failed: Unexpected token (72 ...

What is the process for exporting a plugin from dayjs() in JavaScript?

Currently, I have incorporated the plugin isToday() to enhance the capabilities of dayjs(). Nevertheless, I am uncertain about how to export isToday() in order to utilize it across other files. import isToday from "dayjs/plugin/isToday"; expor ...

Integrating TypeScript into an established project utilizing React, Webpack, and Babel

Currently, I am in the process of integrating Typescript into my existing React, Webpack, and Babel project. I aim to include support for file extensions such as [.js, .ts, .tsx] as part of my gradual transition to Typescript. I have made some progress, b ...

Tips for adding a new property to an array object in TypeScript using a condition

Here is an array object that I have: arr = [ { Name: "ABC", Age: 20}, { Name: "XXX", Age: 15} ]; In Typescript, I am looking to dynamically add a new property called "Flag" with a value of 1 only if the Age is greater than 15. Can someone suggest ...

Issues arise when Typescript fails to convert an image URL into a base64 encoded string

My current challenge involves converting an image to base 64 format using its URL. This is the method I am currently using: convertToBase64(img) { var canvas = document.createElement("canvas"); canvas.width = img.width; canvas.height = img.he ...

Is it possible to lengthen a function in TypeScript?

I am part of a team responsible for maintaining a unique JavaScript library that generates spy functions. These spy functions are designed to allow users to monitor how a function is called, primarily used in the context of unit testing. Our library creat ...