Could there possibly exist a TypeScript generic that can effectively manage types T, T[], and "*" simultaneously?

As a newcomer to generics in typescript, I find it quite perplexing

Is there any way to get the spread operator ... to function correctly in the code snippet below?

The Dilemma

The line [ key: U, ...rest: Reg[U] ] is not behaving as I anticipated

The Query

What am I missing here?

I attempted to resolve this issue but hit a roadblock

Potential Solutions I Haven't Explored Yet

Function Overloading


type Registrar = {
  "item.1": [name: string]
  "item.2": [id: number, ready: "Y" | "N"]
  "item.3": [ok: boolean]
}
type ReKey = keyof Registrar
const transmit = <
  T extends ReKey | ReKey[] | "*" | never = never
>(
  key: T,
  callback: (
    ...args: 
        T extends Array<infer U>
          ? (
            U extends ReKey 
              ? [ key: U, ...rest: Registrar[U] ]
              : never 
          )
          : T extends string 
            ? (
              T extends ReKey 
                ? Registrar[T]
                : T extends "*" 
                  ? Registrar[ReKey]
                  : never 
            )
            : never 
  ) => any
) => { /**  */ }
transmit("item.2", (...args) => {
  const [ 
    arg1,
    //^?const arg1: number 
    arg2,
    //^?const arg2: "Y" | "N" 
  ] = args 
})

transmit(["item.1", "item.2"], (key, ...args) => {
  //                                ^?
  const k = key 
  //    ^?const k: "item.1" | "item.2"

  if (key == "item.1") {
    const [ 
      arg1,
      //^?const arg1: string | number 
      arg2, 
      //^?const arg1: string | number 
    ] = args
  }
  if (key == "item.2") {
    const [ 
      arg1,
      //^?const arg1: string | number 
      arg2,
      //^?const arg2: string | number 
    ] = args 
  }
})

Check out the TypeScript playground for more details: https://tsplay.dev/mxEQ7W

Answer №1

Your specified types are in good order. However, there is an issue with TypeScript not supporting destructured discriminated unions when using a rest property for destructuring. An open feature request exists at microsoft/TypeScript#46680 to address this limitation, but as of now, you'll need to find a workaround.


The code snippet you provided is essentially the same as:

type ParamUnion =
  [key: "item.1", name: string] |
  [key: "item.2", id: number, ready: "Y" | "N"];
  
const f: (...args: ParamUnion) => any =
  (key, ...rest) => {
    if (key === "item.1") {
      rest[0].toUpperCase(); // error!
    } else {
      rest[0].toFixed(); // error!
    }
  };

In this scenario, ParamUnion represents a discriminated union of tuple types where the first element serves as the discriminant.

This addresses the core problem without delving into generics or other complexities like T | T[] | "*" or [ key: U, ...rest: Reg[U] ], which may distract from the actual issue.


To overcome this challenge, one workaround is to avoid using a rest element during destructuring assignment of the ParamUnion type into parameters:

const g: (...args: ParamUnion) => any =
  (key, nameOrId, ready?) => {
    if (key === "item.1") {
      nameOrId.toUpperCase(); // okay
    } else {
      nameOrId.toFixed(); // okay
    }
  };

Alternatively, another approach is skipping the rest element entirely and utilizing a rest parameter instead:

const h: (...args: ParamUnion) => any =
  (...args) => {
    if (args[0] === "item.1") {
      args[1].toUpperCase(); // okay
    } else {
      args[1].toFixed(); // okay
    }
  };

Both of these methods yield the desired results.

Try out the code on the TypeScript 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

Merging objects with identical keys into a single object within an array using Typescript

Here is the array that I am working with: Arr = [{ code: "code1", id: "14", count: 24}, {code: "code1", id: "14", count: 37}] My objective is to consolidate this into a new array like so: Arr = [{ code: "code1& ...

Running a Typescript ES6+ server-side with .NET Core and Node.js is made easy with Jering.Javascript.NodeJS. Learn how to set

I am trying to run some TypeScript code written in ES2020 on the server side. Currently, I have an ASP.NET Core application running, so my idea was to execute JavaScript via Jering.Javascript.NodeJS and Node.js. I started with a simple example. var test = ...

MXGraph has an issue where edges fail to be redrawn after being moved

Perhaps this question may seem trivial, but I am facing an issue in my code and seeking guidance from the community. I am working on a javascript mxGraph application within Angular7. Specifically, I have modified the ports.html example for my project. Wh ...

Restricting a generic parameter to a combination type in Typescript

Is there a method in Typescript to restrict a generic parameter to only accept a union type? To clarify my question, I wish that T extends UnionType would serve this purpose: function doSomethingWithUnion<T extends UnionType>(val: T) {} doSomethingW ...

Can I leverage getStaticProps and getStaticPaths within a page component that employs dynamic routing without prior knowledge of the IDs?

I have created a fully static site generation (SSG) app where the backend cannot be accessed during build time. All the data is specific to user permissions, so I cannot specify paths in the getStaticPaths method required for dynamic routed components us ...

Why should one bother with specifying types when defining a variable in Typescript?

As someone new to Typescript, I've come to appreciate its many advantages when working on larger applications and with multiple team members :) Imagine you have the following TypeScript code: declare const num = 5: number; Why is this better than: ...

When working with Angular 5, the question arises: how and where to handle type conversion between form field values (typically strings) and model properties (such

As a newcomer to Angular, I am struggling with converting types between form field values (which are always strings) and typed model properties. In the following component, my goal is to double a number inputted by the user. The result will be displayed i ...

Ways to fix a TypeScript syntax-checker error in Visual Studio

I am currently facing an issue while attempting to showcase an array using Angular and TypeScript. The error message that I encounter is quite perplexing and I am unsure of its meaning. Upon hovering my cursor over the Goal that is underlined in red, the ...

Best practices for distinguishing between template and style for mobile and desktop in Angular 2 components

Creating templates for mobile and desktop requires unique designs, but both share common components. To ensure optimal display on various devices, I plan to store separate templates and designs for mobile and desktop in distinct files. The goal is to inc ...

Unexpected error: Angular 4 TypeScript validation issue - An object literal can only define recognized properties

excellent customer service import {Account} from '../models/Account'; export class AccountingService { domain: string = "http://localhost:3000/api"; constructor(private http: HttpClient) { } getAccounts(){ return this.http.get&l ...

Is there a way to utilize const assertions to retrieve the explicit types from objects nested at various levels?

In reference to this question, the previous structure had a depth of 2: const grandkids = { Karen: { Ava: ['Alice', 'Amelia'], Emma: ['Sarah'], }, Mary: { Sophia: ['Grace'], }, } as const; To ext ...

Transformation of Python code into Blockly blocks

As the founder of edublocks.org, I am interested in adding Python to Blocks functionality on the platform. At the moment, users can only transition from Blocks to Python. Is there anyone who has experience with this and can provide guidance on how to achi ...

Tips for extracting subdomains from URLs

Looking for a way to extract only the 'http://abc' part from a URL like http://abc.xyz.com, unfortunately getting the full 'http://abc.xyz.com'. I attempted using: windw.location.origin Do I need to implement an additional method to a ...

My intention is to shift the TypeScript object to a higher level within the hierarchy

Currently, I am working with TypeScript and my main goal is to transform the object structure in ①props into the structure shown in ②. ① Test {props: {…}} props: avatarUrl: "/test.jpg" id: 1 name: "test" ...

Guide on integrating TypeScript with the Esri Leaflet JavaScript Plugin

I'm working on an Aurelia project in TypeScript that incorporates Leaflet for mapping. So far, I've been able to use typings for Leaflet itself, but the esri-leaflet plugin is only available in JavaScript. How can I import and utilize this JavaSc ...

Which components can be interacted with in Protractor?

The element I am attempting to engage with utilizes the ng-sortable attribute and consists of a few draggable and sort-able divs. await viewTransaction.getEl('div#dragdrop-boundary').sendKeys(protractor.Key.ARROW_DOWN); Failed: element not inte ...

Using Firebase with Angular 4 to fetch data from the database and show it in the browser

Currently diving into Angular 4 and utilizing Firebase database, but feeling a bit lost on how to showcase objects on my application's browser. I'm looking to extract user data and present it beautifully for the end-user. import { Component, OnI ...

Ways to resolve TypeScript type issues that are functioning correctly with one type but encountering errors when used with functions

When the route function encounters the middlewares parameter type, it always throws an error. However, no error occurs if the type is used directly, as seen in lines 72 and 75. Errors will occur on lines 107 and 98. abstract class BaseMiddleware< In ...

A powerful trio: Axios, Typescript, and Promises

I am facing a TypeScript dilemma. I have a REST method that is being called within my http library by Vue action. I want the resolve() method to return the typed array, but if I do not convert it within the action.ts "then" method, I get a '.length do ...

When the imagepath in an Angular application is not updated, it will revert to null

Below is the code snippet that I am currently working on: <div class="col-sm-4 pl-5"> <img attr.src="{{item?.imagePath}}" required height="200" width="200"> </div> In my TypeScript file (ts), I have the following function: editBlog ...