TypeScript does not restrict generic types

Consider this scenario with a basic parser:

const str = "String to test";
let pos = 0;
function eat(
  ...args: (
    | [RegExp, (result: RegExpExecArray) => boolean]
    | [string, (result: string) => boolean]
    | [string[], (result: number) => boolean]
  )[]
) {
  const oldPos = pos;
  for (const [test, callback] of args) {
    if (test instanceof RegExp) {
      test.lastIndex = pos;
      const match = test.exec(str);
      if (match) {
        pos = test.lastIndex;
        if (callback(match)) return true;
        break;
      }
    } else if (typeof test === "string") {
      if (str.substr(pos, test.length) === test) {
        pos += test.length;
        if (callback(test)) return true;
        break;
      }
    } else {
      const temp = test.findIndex(item => str.substr(pos, item.length) === item);
      if (temp != -1) {
        pos += test[temp].length;
        if (callback(temp)) return true;
        break;
      }
    }
  }
  pos = oldPos;
  return false;
}
if (eat(
  [/string/iy, result /* RegExpExecArray */ => {
    // ...
    return true;
  }],
  [["foo", "bar"], result /* number */ => {
    // ...
    return true;
  }]
)) console.log("Matched");

function example(arg: ["string", string, number] | ["boolean", boolean, bigint]) { if (arg[0] === "string") { Math.floor(arg[2]); // satisfies the compiler } }

However, this method may be cumbersome when making a call. It also doesn't seem to work in my specific case.

I've experimented with generics and function overloads, but they either do not work or do not meet my requirements (especially because I mix different types of argument arrays when calling the function).


Any suggestions on how to address this issue? Should I consider raising it on GitHub's issue page if it appears to be a bug?

You can find additional experiments related to this problem in this typescript playground.

Answer №1

If you want to ensure your code is type-safe, you can create a type guard that will assist you in achieving this goal:

const str = 'String to test';
let pos = 0;

type TCallback<TTest, TParam> = [TTest, (result: TParam) => boolean];

function isCallback<TTest, TParam>(
    callback: TCallback<any, any>,
    validate: (test: TTest) => boolean
): callback is TCallback<TTest, TParam> {
    return validate(callback[0]);
}

function eat(
    ...args: (
        | TCallback<RegExp, RegExpExecArray>
        | TCallback<string, string>
        | TCallback<string[], number>
    )[]
) {
    const oldPos = pos;
    for (const arg of args) {
        if (isCallback<RegExp, RegExpExecArray>(arg, (test) => test instanceof RegExp)) {
            const [test, callback] = arg;
            test.lastIndex = pos;
            const match = test.exec(str);
            if (match) {
                pos = test.lastIndex;
                if (callback(match)) return true;
                break;
            }
        } else if (isCallback<string, string>(arg, (test) => typeof test === 'string')) {
            const [test, callback] = arg;
            if (str.substr(pos, test.length) === test) {
                pos += test.length;
                if (callback(test)) return true;
                break;
            }
        } else if (isCallback<string[], string>(arg, (test) => test instanceof Array)) {
            const [test, callback] = arg;
            const temp = test.findIndex((item) => str.substr(pos, item.length) === item);
            if (temp != -1) {
                pos += test[temp].length;
                if (callback(temp)) return true;
                break;
            }
        }
    }
    pos = oldPos;
    return false;
}

if (eat(
    [/string/iy,
    (result: RegExpExecArray) => {
        // ...
        return true;
    }],
    [['foo', 'bar'],
    (result: number) => {
        // ...
        return true;
    }]
))
    console.log('Matched');

It's worth noting that by specifying the parameters' types in the isCallback method, TypeScript can accurately determine the return type based on the confirmation provided.

In addition, within the typeguarded condition, I extract the variables test and callback, as it is the specific location where their types are certain.

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

How about utilizing a map with arrays for this object?

Currently, I am delving into the realms of JS and TypeScript with a focus on generating separate React components for specific items. Allow me to introduce you to an example object: var regions = { NA: ["US", "ABC1"], EU: ["GB", "LX", "IT"], F ...

Develop an object's attribute using form in the Angular 5 framework

I am looking to create an object for a location that includes two parameters. While I can easily create an array of strings using FormGroup, I am unsure of how to create an object with two parameters nested inside it. Below is the code snippet I currently ...

"Learn how to dynamically update a value based on the selected option in a drop-down menu in Angular

Hello everyone. I am working on an angular project that includes a product page. Some products on this page can have multiple types, like so: Product ID-1 Type ID-1 Price-$10 Product ID-1 Type ID-2 Price-$15 Product ID-1 Type ID-3 Price-$25 In this sce ...

Issue with MathJax rendering within an Angular5 Div that's being observed

I am trying to figure out how to enable MathJax to convert TeX to HTML for elements nested within my div. Here is the current content of app.component.html: <p> When \(a \ne\) It works baby </p> <div class="topnav"> ...

The Excel Match function is experiencing issues when used in conjunction with the MS-Graph API

Recently, I've encountered an issue with sending a match-function post request to an Excel workbook using the MS-Graph API. Instead of receiving the value of the first column that contains the lookup value, I'm getting the value from the second c ...

Typescript-optimized Npm library utilizing the module-alias feature

As I work on creating an npm library with Typescript, I have made use of the paths setting in tsconfig.json and the module-alias tool to streamline my imports, allowing me to use syntax like import * from '@/utils'. However, I have encountered an ...

Issue: Incorrect hook usage. Hooks are designed to be used within the body of a function component. This error may occur due to one of the following reasons: 1

I've reviewed similar questions and attempted to apply the solutions provided, but it seems I'm missing something specific to my situation. My goal is to streamline my code by importing headers from a utils file and using them across different AP ...

There is a compatibility issue between the module and the engine "node" in this instance

Upon running the command npx create-react-app my-awesome-react-app --template typescript, I encountered the following yarn error: Error [email protected]: The engine "node" is incompatible with this module. Expected version "^6 || ^7 || ^8 || ^9 || ^10 || ...

What is the best way to initiate a refetch when the need arises to follow a different path?

I have encountered a situation where I am able to pass the refetch function on a child component. However, an issue arises when transitioning to another page and implementing redux. This is particularly problematic when attempting to open a dialog for a ne ...

Issue 1068: Attribute not found within angular 2 (Ahead of Time Compilation)

I am currently learning Angular 2 and trying to create a "User Register" form. However, I encountered an error stating "Property does not exist on type" during Phone number validation. I am using both JIT and AOT compilers. With the JIT compiler, my user ...

The element type 'HTMLElement' does not contain a property named 'pseudoStyle'

Currently experimenting with adjusting the height of a pseudo element using Typescript. An error is popping up in my IDE (vscode) as I go along. This is the code snippet I am working with. // choose element let el: HTMLElement = document.getElementById( ...

Sacrificing type safety versus retaining type safety

I'm curious to know what sets apart these two approaches when declaring the status property. I understand that the second version maintains type safety, but how exactly does it achieve this? export type OwnProps = { id: number; name: string; sta ...

Exploring Model Object Properties with ngFor in Angular

Currently, I am developing an Angular application with a Model object that consists of properties of various types. My goal is to loop through these properties in the component and generate form fields for each property. Unfortunately, the implementation i ...

The concept of contextual typing within Typescript is essential for ensuring proper

Snippet: class A { x: number; } class B extends A { y: number; } var f1: { (y: A): void } | { (y: B): void }; f1 = (y)=>{} // y :any var f2: { (x: number): (y: A) => void } | { (x: number): (y: B) => void }; f2 = ((x) => { return (y ...

Unable to subscribe due to the return value being an AnonymousSubject rather than an Observable

I am facing an issue in Angular 4 where I am attempting to retrieve details from a specific user when clicking on the 'user link profile'. However, I am unable to subscribe to my function because the return value of switchMap is AnonymousSubject ...

Issue: The object type '{ children: Element; }' does not share any properties with the type 'IntrinsicAttributes & Pick<ClassAttributes<Layout>&Props, "ref" | "key">'

Encountering an error message: Type '{ children: Element; }' does not share any properties in common with type 'IntrinsicAttributes & Pick<ClassAttributes & Props, "ref" | "key">'. I recently started learning ReactJS with ...

How can we stop the parent modal from closing if the child component is not valid?

I have a main component with a modal component that takes another component as a parameter. The child modal component has some logic where I need to check if the child component is valid before closing the modal. const MainComponent: FC<IProps> => ...

Is it possible to smoothly transition to the next step in PLAYWRIGHT testing if a button click is not an option?

My question is whether it's possible to attempt a click action on a button, and if the button is not present on the page, have the test skip that action without getting stuck or throwing an error, and continue to the next one. To provide more context, ...

Unable to send back transaction response from callback to the user

Our payment processing system utilizes the authorize.net Node SDK. We have implemented a Firebase callable function to manage payment requests but are encountering difficulties in retrieving the transaction response. The issue lies within the following co ...

Get the file without specifying type in request - Angular

Is it possible to download a file solely through the response without specifying a responsetype in the header? I am looking for a way to download the file without including any additional parameters in the header. ...