"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

The ASP.NET Core Web API successfully sends back a response, but unfortunately, the frontend is only seeing an empty value along with a status code of 200 (OK)

Currently, I am delving into the world of web APIs and have stumbled upon a perplexing issue that requires assistance. I have an active ASP.NET Core Web API at the backend, while at the frontend, an Angular application (running on version 15.1.5) is in pl ...

Understanding Typescript in Next.js components: Deciphering the purpose behind each segment

Consider the following function: type User = { id: string, name: string } interface Props { user: User; } export const getUserInfo: GetUserInfo<User> = async ({ user }: Props) => { const userData = await fetchUser(user.id); return ...

Enhancing the appearance of the Mui v5 AppBar with personalized styles

I am encountering an issue when trying to apply custom styles to the Mui v5 AppBar component in Typescript. import { alpha } from '@mui/material/styles'; export function bgBlur(props: { color: any; blur?: any; opacity?: any; imgUrl?: any; }) { ...

The module 'DynamicTestModule' has imported an unexpected directive called 'InformationComponent'. To resolve this issue, please include a @NgModule annotation

Even though I found a similar solution on Stackoverflow, it didn't resolve my issue. So, let me explain my scenario. When running the ng test command, I encountered the following error: Failed: Unexpected directive 'InformationComponent' i ...

Reduce the size of a container element without using jquery

In my Angular application, I have structured the header as follows: -- Header -- -- Sub header -- -- Search Box -- -- Create and Search Button -- -- Scroll Div -- HTML: <h1> Header </h1> <h3> Sub header </h3> <div class="s ...

Using React.ReactNode as an argument in Storybook

This is a unique button component type that I have created import React from 'react' export type ButtonProps = { label: string; color?:'primary' | 'secondary' | 'tertiary'; size?:'mobile' | 'tabl ...

Optimal data structure for storage in a Next.js project with TypeScript

What is the appropriate data type for store in export let store: any; other than any? I have used @ts-igore to suppress TypeScript errors on the last line. How can I address the TypeScript error for that as well? I mentioned the boilerplates utilized in ...

How can I seamlessly combine CoffeeScript and TypeScript files within a single Node.js project?

When working on a server-side node project utilizing the standard package.json structure, what is the best way to incorporate both Coffeescript and Typescript files? It's crucial that we maintain the availability of npm install, npm test, and npm sta ...

Restrict the number of subscriptions allowed for an rxjs observable

Seeking a replacement for observable, subject, or event emitter that allows only one subscription at a time. The first subscriber should have priority to execute its subscribe function, with subsequent subscribers waiting their turn until the first unsubsc ...

Sending data using formData across multiple levels of a model in Angular

I have a model that I need to fill with data and send it to the server : export interface AddAlbumeModel { name: string; gener: string; signer: string; albumeProfile:any; albumPoster:any; tracks:TrackMode ...

Inheritance in Angular with TypeScript Using Generic Types

Looking for some assistance from the angular2 / typescript experts out there to guide me in the right direction before I lose my mind :-) Here's what I'm trying to achieve: I want to create a parent class that implements its own defined parent ...

The challenge with the Optional Chaining operator in Typescript 3.7@beta

When attempting to utilize the Typescript optional chaining operator, I encountered the following exception: index.ts:6:1 - error TS2779: The left-hand side of an assignment expression may not be an optional property access. Here is my sample code: const ...

Using a custom TypeScript wrapper for Next.js GetServerSideProps

I developed a wrapper for the SSR function GetServerSideProps to minimize redundancy. However, I am facing challenges in correctly typing it with TypeScript. Here is the wrapper: type WithSessionType = <T extends {}>( callback: GetServerSideProps&l ...

The idiom 'listen' is not recognized within the context of type 'Express'. Try using a different property or method to achieve the desired functionality

Encountering an error in VS Code while working on an Angular 13 app that utilizes Angular Universal for Server Side Rendering. The specific error message is: Property 'listen' does not exist on type 'Express'.ts(2339) This error occurs ...

Always deemed non-assignable but still recognized as a universal type?

I'm curious about why the never type is allowed as input in generic's extended types. For example: type Pluralize<A extends string> = `${A}s` type Working = Pluralize<'language'> // 'languages' -> Works as e ...

React TypeScript: The properties of 'X' are not compatible. 'Y' cannot be assigned to 'Z' type

I am currently working on a React-TypeScript application, specifically creating a component for inputting credit card numbers. My goal is to have the FontAwesome icon inside the input update to reflect the brand image as the user enters their credit card n ...

The Ionic framework has a defined variable

In my code, I have initialized a variable inside the constructor like this: constructor(public http: HttpClient) { this.data = null; this.http.get(this.url).subscribe((datas: any) => { this.dbUrl = datas[0].db_url2; console.log(this ...

My HTML files are not recognizing the IONIC Property within their own objects

As I delve deeper into understanding Angular and Ionic, a peculiar issue has arisen for which I seek a solution. I have several export classes containing HTML forms. In each corresponding .ts file, I declare a variable and import the relevant model to bin ...

The React useEffect() hook causing an infinite re-render when trying to fetch all data regardless of

Recently, I've begun diving into React and utilizing the useEffect hook to fetch news and events from a database upon page load. However, when attempting to add a loading spinner, I encountered an unexpected infinite loop issue that has left me scratc ...

Passing a method from a component to a service in Angular 9

Recently, I've been working on some websocket code that involves sending a message to the server and receiving a reply. The current implementation is functional, but I'm looking to refactor it by encapsulating it within a service and then callin ...