Determine the data type of a property by referencing the data type of another property within an array of objects using TypeScript

I am working with a specific type of interface called Route that consists of name and path properties.

interface Route {
    name: string,
    path: string
}

const routes = [
    {
        name: "first",
        path: "/first"
    },
    {
        name: "second",
        path: "/second"
    }
] as const

My goal is to develop a function that can retrieve the path for a given route based on its name.

const to = <Routes extends ReadonlyArray<Route>, CurrentRoute extends Routes[number]>(routes: Routes, name: CurrentRoute["name"]): CurrentRoute["path"] => {
    // Implementation details are not important here
    return "" as CurrentRoute["path"]
}

const path = to(routes, "first")
// Currently returns a union instead of a string

What I need is for this function to accurately return a single string inferred from the corresponding object's name:

  • "/first" should be returned by TypeScript when calling the function with "first"
  • "/second" should be returned by TypeScript when calling the function with "second"
  • ...

The actual implementation of the function is trivial; what I am seeking is a solution within TypeScript's type system.

Link to reproduction

Answer №1

The issue with your version is the lack of an inference site for the CurrentRoute type argument. Unfortunately, generic types cannot be inferred from indexed access types like CurrentRoute["name"]. There was a pull request at microsoft/TypeScript#20126 that aimed to address this but did not get implemented.

To resolve this issue, you can directly associate the name property with a generic type parameter (let's call it N) and use it to compute the type for your CurrentRoute. Here is one way to achieve this:

const to = <R extends Route, N extends R["name"]>(
    routes: readonly R[], name: N): Extract<R, { name: N }>["path"] => {
    // Implementation details are irrelevant here
    return null!;
}

Since the order of elements in routes does not matter in your example, I have replaced your Routes type parameter representing the entire array with just R representing the union of its members. The type parameter N is constrained to R["name"], which is the union of name property types in R.

The return type utilizes the Extract<T, U> utility type to extract the member of the union with a name property of type N and then accesses its path property type.

Let's test if it works:

const path = to(routes, "first");
// const path: "/first"

const path2 = to(routes, "second");
// const path2: "/second"

Everything seems to be functioning correctly!

Link to Playground

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

Angular 8 is throwing an error stating that the property 'token' is not recognized on the 'Object' data type

As a newcomer to Angular, I have been following the MEAN Stack - Mean Auth App Tutorial in Angular 2. However, since I am using Angular 8, some of the code is deprecated due to recent updates. I have encountered an issue with the code bel ...

A guide on implementing code sharing in NestJS using Yarn Workspaces

I'm currently working on a proof of concept for a basic monorepo application. To structure my packages, I've decided to use Yarn Workspaces instead of Lerna as it seems more suitable for my needs. One of the packages in my setup is shared, which ...

Despite being logged, the current value of firebase.auth().currentUser in Firebase is null

I have coded a query in my service.TS file that displays the "state" of items based on the UID of the logged-in user: getLists(): FirebaseListObservable<any> { firebase.auth().onAuthStateChanged(function(user) { if (user) {console.log("blah", fir ...

Exploring how NestJS can serialize bigint parameters within DTOs

I have data transfer objects (DTOs) with parameters that are of type bigint. However, when I receive these DTOs, the parameters always have a type of string. Here is an example: @Get("") async foo(@Query() query: Foo) { console.log(typeof Foo ...

Navigating through different components within a single page

Each segment of my webpage is a distinct component, arranged consecutively while scrolling e.g.: <sectionA></sectionA> <sectionB></sectionB> <sectionC></sectionC> All the examples I've come across involve creating ...

How can I properly include DefinitelyTyped TypeScript definition files in a .NET Core project?

Previously, under asp.net for .net framework, I utilized NuGet to incorporate TypeScript type definitions from third-party libraries (*.d.ts files) provided by DefinitelyTyped. However, with the shift to .NET Core, it seems that NuGet is no longer recommen ...

Pinia store encountering a Typescript Vue component issue: error message "The property 'testStore' does not exist on the 'CreateComponentPublicInstance' type."

Within my <script setup> block, I have imported my testStore. However, whenever I attempt to use this.testStore.name in my Vue file, Vetur displays the following error: Property 'testStore' does not exist on type 'CreateComponentPublic ...

After updating the state in a Reducer with Redux Toolkit, make sure to utilize a

Issue: Seeking efficient code writing methods. Here is a detailed example of my Redux Toolkit slice below: import { createSlice } from '@reduxjs/toolkit'; import { setCookie } from '../../utils/storageHandler'; const initialState = { ...

How can one click the button within the expanded dropdown while hovering over the navigation item in Angular and Bootstrap?

Issue Description: Upon hovering over a navigation item, the dropdown container is displayed but it's not clickable. Desired Behavior: Hovering over a navigation item should display the dropdown container and allow clicking on its contents. Furthermo ...

Top tips for handling HTML data in JSON format

I'm looking to fetch HTML content via JSON and I'm wondering if my current method is the most efficient... Here's a sample of what I'm doing: jsonRequest = [ { "id": "123", "template": '<div class=\"container\"&g ...

Using an image as a button in Vue.js: A step-by-step guide

I'm currently working on creating a login button within a single-file-component using Vue.js in my Rails application with a Vue.js front-end. The purpose of this button is to redirect users to an external login page when clicked. I am wondering how I ...

The interface is incompatible with the constant material ui BoxProps['sx'] type

How can I make the interface work for type const material ui? I tried to register an interface for sx here, but it keeps giving me an error. import { BoxProps } from '@mui/material'; interface CustomProps { sx: BoxProps['sx&apo ...

The Angular performance may be impacted by the constant recalculation of ngStyle when clicking on various input fields

I am facing a frustrating performance issue. Within my component, I have implemented ngStyle and I would rather not rewrite it. However, every time I interact with random input fields on the same page (even from another component), the ngStyle recalculate ...

Using Angular and Typescript to implement a switch case based on specific values

I am attempting to create a switch statement with two values. switch ({'a': val_a,'b': val_b}){ case ({'x','y'}): "some code here" break; } However, this approach is not functioning as expected. ...

Having trouble importing or resolving files with ts-loader or css-loader?

Struggling to incorporate css modules by utilizing style-loader and css-loader in my project. I am facing difficulties understanding the root cause, unsure if it's ts-loader or css-loader to blame. webpack.config.js const path = require('path&a ...

Ways to pass data to a different module component by utilizing BehaviourSubject

In multiple projects, I have used a particular approach to pass data from one component to another. However, in my current project, I am facing an issue with passing data from a parent component (in AppModule) to a sidebar component (in CoreModule) upon dr ...

Can you explain the concept of widening in relation to function return types in TypeScript?

Recently, I've observed an interesting behavior in TypeScript. interface Foo { x: () => { x: 'hello' }; } const a: Foo = { x: () => { return { x: 'hello', excess: 3, // no error } }, } I came acro ...

Utilizing Node.JS and Typescript to correctly define database configuration using module.exports

I am currently utilizing Mongoose in my node.js application, which is written in Typescript. The Mongoose documentation provides clear instructions on how to connect to their database like this, but I prefer to have the configuration stored in a separate ...

Creating Dynamic Graphs using Angular and Chart.js with Array Values

I have implemented ChartJS-2 to visualize a graph displaying an array of user activities, but it appears distorted: import { Component, OnInit, Input } from '@angular/core'; import { ChartOptions, ChartType, ChartDataSets } from 'chart.js ...

Discovering new bugs in VSCode Playwright Tests but failing to see any progress

This morning, everything was running smoothly with debugging tests. However, after a forced reboot, I encountered an issue where it seems like the debugger is running, but nothing actually happens. This has happened before, but usually resolves itself. Unf ...