"Utilizing generic types with the 'extends' keyword to input arguments into a function that requires a more specific

I recently tried out the TypeScript playground and came across a puzzling issue that I can't seem to wrap my head around.

Below is the code snippet:

type Foo = {
    t: string;
}

type Bar = string | {
    date: Date;
    list: string;
}

function test<T extends Bar | Foo>(items: T[]) {
    console.log(items);
    test2(items);
}

function test2(items: Bar[] | Foo[]) {
    console.log(items);
}

While testing the code, an error surfaced when calling test2 within test, indicating that T[] is not a valid type for the function parameter of test2.

What could be causing this discrepancy?

Answer №1

Displayed below is the compiler error relating to the code snippet you provided:

function test <T extends Bar | Foo>(items: T[]): void {
  console.log(items);
  test2(items);
  //    ~~~~~
}
Argument of type 'T[]' cannot be assigned to a parameter of type 'Bar[] | Foo[]'.
  Type 'T[]' is not compatible with type 'Bar[]'.
    Type 'T' is not assignable to type 'Bar'.
      Type 'Foo | Bar' cannot be assigned to type 'Bar'.
        Type 'Foo' cannot be assigned to type 'Bar'.
          Type 'T' is not assignable to type '{ date: Date; list: string; }'.
            Type 'Foo | Bar' cannot be assigned to type '{ date: Date; list: string; }'.
              Type 'string' cannot be assigned to type '{ date: Date; list: string; }'. (2345)

This means that currently, your test function expects an array of values where each individual value can be either of type Foo or Bar. It allows for a mixed array containing elements of both types.

For example, this works fine:

TS Playground

const array = [
  { t: 'hello' },
  'world',
  { date: new Date(), list: 'str' },
];

test(array); // no issues

However, your test2 function requires an array argument containing exclusively Foo elements or exclusively Bar elements:

function test2 (items: Bar[] | Foo[]): void;

Hence, when passing the same array to test2, the error arises:

TS Playground

const array = [
  { t: 'hello' },
  'world',
  { date: new Date(), list: 'str' },
];

test2(array);
//    ~~~~~
// A very similar compiler error happens here

To resolve this, adjust the parameter of the test2 function to accept an array with mixed Foo and Bar elements, like so:

(Bar | Foo)[]

When combined, the updated functions will look like this:

TS Playground

type Foo = { t: string };

type Bar = string | {
  date: Date;
  list: string;
};

function test <T extends Bar | Foo>(items: T[]): void {
  console.log(items);
  test2(items); // no errors now
}

// function test2 (items: Bar[] | Foo[]) {
function test2 (items: (Bar | Foo)[]): void {
  console.log(items);
}

Alternatively, if you prefer a stricter test function that only accepts arrays with exclusively Foo or Bar elements, modify it as follows while maintaining compatibility with test2:

TS Playground

function test <T extends Bar[] | Foo[]>(items: T): void {
  console.log(items);
  test2(items); // no issues
}

Answer №2

If you modify the signature of `test2` to the following, it will be considered acceptable:

function test2(items: Array<Bar|Foo>)

The reason being that

test<T extends Bar | Foo> (items: T[])

implies that items can be an array of both types A and B simultaneously (while your intention is likely to have either an array of A or an array of B), and `test2` requires a unified array of one type or the other.

Another solution could be:

function test<A extends Bar, B extends Foo> (items: A[] | B[]) {
    console.log(items);
    test2(items);
}

function test2(items: Foo[] | Bar[]) {
    console.log(items);
}

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

Error: Unexpected top-level property "import/order" in ESLINT configuration

I'm in the process of setting up a guideline to include one blank line between internal and external imports. .eslintrc.json: { "parser": "@typescript-eslint/parser", "env": { "es6": true, " ...

Passing data from child components to parent components in NextJs using Typescript

I have created a new component <ConnectWallet setConnected={(t: boolean) => console.log(t)}> <>test</> </ConnectWallet> The component is initialized as follows import { useState, useEffect } from ' ...

Parent component interacting with child component

In my application, I have a header component along with registration and login components. The selector of the header component is used in both the login and registration components. There is also a button in the header that displays as a login button if t ...

Struggling to integrate D3.js with React using the useRef hook. Any suggestions on the proper approach?

I'm currently working on creating a line chart using d3.js and integrating it into React as a functional component with hooks. My approach involved utilizing useRef to initialize the elements as null and then setting them in the JSX. However, I encou ...

The functionality of NgbModal in ng-bootstrap is experiencing issues and becoming unresponsive in ng-bootstrap version 15 and Angular version 16

Currently, I am in the process of upgrading my Angular app from version 15 to version 16. Following the documentation, I have updated the @ng-bootstrap/ng-bootstrap package to version 15. However, after this update, I am facing issues with the NgbModals no ...

The value stored in Ionic Storage will only be visible on the HTML page after a refresh is performed

After updating my Ionic Storage values, they are not showing up on the HTML page until I reload it. I have researched similar issues faced by others, but the solutions I found either do not work or are no longer applicable due to changes in the Ionic versi ...

Typescript: Omitting mandatory fields from a type

Can anyone help me with defining the type ExcludeAllRequiredProps<T> in the code below to exclude all required properties? Any assistance would be greatly appreciated. Thank you. type A = { a: number, b: number, c?: number, d?: number } typ ...

Managing state on the login page functions properly, although there is a minor inconvenience of having to click the login button twice after entering the username and password

In Login.tsx, I store user/pass info in a useState called login and pass it up to App.tsx. I then store the access property from login useState to access useState inside App.tsx. While this does technically work, there is an issue where it seems to be one ...

Leverage the VTTCue Object in an Angular2 project using Typescript

I am looking to dynamically load subtitles onto a video. let subs:TextTrack = video.addTextTrack('subtitles'); for (let dataSrt of dataSrts) { let cue: any = new VTTCue( dataSrt['startTime'], da ...

Shift the Kid Element to an Alternate Holder

Currently, I am working on a project in Angular version 10. Within this app, there is a component that can be shared and will utilize the provided content through ng-content. Typically, this content will consist of a list of items such as divs or buttons. ...

Struggling to locate the ID linked to a specific ObjectId and encountering issues with the import function?

Can someone help me with this issue? Error Message: ERROR TypeError: answerID.equals is not a function I am unsure why I am getting this error. Here is the code snippet: import { ObjectId } from 'bson'; export class Person{ personID: Objec ...

Can we guarantee that the key and its corresponding value are both identical strings in Typescript?

Is it possible to enforce the key of a Record to be the same as the inner name value in a large dataset? interface Person<T> { name: T title: string description: string } type People = Record<string, Person<string>> // example dat ...

Creating a custom autocomplete search using Angular's pipes and input

Trying to implement an autocomplete input feature for any field value, I decided to create a custom pipe for this purpose. One challenge I'm facing is how to connect the component displaying my JSON data with the component housing the autocomplete in ...

"Troubleshooting: Why are errors not appearing in ts-node

Whenever I encounter an error in my code while compiling with ts-node, the error does not seem to appear in the console. For instance:let data = await fs.readFileSync(path); In the following code snippet, I am using "fs" to read a file by passing a path ...

Encountering the ExpressionChangedAfterItHasBeenCheckedError error during Karma testing

Testing out some functionality in one of my components has led me to face an issue. I have set up an observable that is connected to the paramMap of the ActivatedRoute to retrieve a guid from the URL. This data is then processed using switchMap and assigne ...

How can I incorporate multiple quality sources into a flowplayer using Angular?

Is there a way to add multiple sources with different qualities like 1080p, 720p etc.? Thank you in advance. flowplayer('#my_player', { src: '../assets/videos/video_1080p.mp4', // title: 'This is just demo&apo ...

Describing the implementation of UNO out parameters within definitions

The UNO API offers support for inout and out parameters. In this scenario, a variable is passed into a function, the function modifies the variable, and the updated value of the variable can be accessed outside the function. Javascript lacks native suppor ...

What is the best way to access a private class variable within the sockent.on function in Angular?

export class AppComponent { title = 'my-app'; constructor(private notifyService : NotificationService) {} ngOnInit() { socket.on("laravel_database_chat:test", function(message){ //I AM ATTEMPTING TO INVOKE THE NOTIF ...

Develop a versatile class for storing an array of key-value pairs (dictionary) using TypeScript

I am looking to implement a Dictionary class in my application. I have created a class with an Array of KeyValuePair to store my list. export class KeyValuePair<TKey, TVal>{ key:TKey; value:TVal; constructor(key:TKey, val:TVal){ this.key = key; ...

In Typescript, it is not possible to assign the type 'any' to a string, but I am attempting to assign a value that is

I'm new to TypeScript and currently learning about how types function in this language. Additionally, I'm utilizing MaterialUI for this particular project. The issue I'm encountering involves attempting to assign an any value to a variable ...