Unusual function call patterns observed in generic classes

After compiling the code below, it runs without any issues

interface A {
  x: string;
}
interface B {
  x: string;
}
type C<D extends A> = D | B;

declare function funcA<D extends A, E extends C<D> = C<D>>(): E | E[] | undefined;
declare function funcB<D extends A, E extends C<D> = C<D>>(param?: E | E[]): void;

const varX = funcA();
funcB(varX);

However, interesting things start happening when the function funcB is enclosed within a class:

export class MyClass<D extends A, E extends C<D>> {
  functionF(param?: E | E[]): void {}

  functionG() {
    const varY = funcA();
    this.functionF(funcA());
    this.functionF(varY);
  }
}

According to the code, the function MyClass.functionF should be the same as function funcB. Nonetheless, an error pops up at the line this.functionF(varY). Interestingly, the line this.functionF(funcA()) compiles without any issues.

Argument of type 'B | A | (B | A)[] | undefined' is not assignable to parameter of type 'E | E[] | undefined'.
  Type 'B' is not assignable to type 'E | E[] | undefined'.
    Type 'B' is missing properties like length, pop, push, concat, and more which are required by type 'E[]'.

This error is perplexing because type E should be equivalent to D | A, so how come A cannot be assigned to E?

Answer №1

The discrepancy arises from the presence of type parameters on a function compared to type parameters on a class. To simplify, I will eliminate the Q[] option on a, b, and f as the same behavior persists without it.


When assigning x = a(), Typescript must deduce a type for the variable x. This necessitates selecting specific types for the type variables N and Q; in the absence of other limitations, their upper limits are chosen, resulting in x being assigned the type M | W | undefined. It's crucial to note that the compiler has the flexibility to designate the upper limit D<M> as the concrete type for Q.

Subsequently, in the invocation b(x), Typescript also has the freedom to assign N = M and Q = D<M>, hence avoiding any type errors.


Conversely, within the class, the assignment y = a() follows the same process, leading to y having the type M | W | undefined. Nevertheless, during the call to f(y), Typescript lacks the liberty to opt for N = M and Q = D<M> since these N and Q are class-type parameters. Inferring N = M and Q = D<M> here could lead to potential inconsistency, as:

  • an individual could define class M2 implements M { ... } and instantiate a C<M2, D<M2>> object.
  • someone might declare
    type D2<N extends M> = D<N> & { ... }
    and create a C<M, D2<M>> object.

Therefore, in the call f(y), assigning y of type M | W | undefined to f's parameter of type Q is deemed unsafe. This is because if, for instance, Q = D<M2>, or Q = D2<M>, or some other scenario arises, y does not match Q nor is a subtype of Q.

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

ES7 Map JSON introduces a new feature by using square brackets

Currently, I am utilizing core-js for the Map collection because it appears that ES7 Map includes a Map to JSON feature that is absent in ES6 Map. (ES6): JSON.stringify(new Map().set('myKey1', 'val123').set('myKey2', 'va ...

Angular asynchronous testing with Observable using karma

I am currently working on testing an asynchronous scenario. Here is a snippet of my component: ngOnInit(private service: MyService) { this.isLoading = true; this.service.getData().subscribe((data) => { this.data = data; this.isLoa ...

Enhance your FullCalendar experience with React by displaying extra information on your calendar

I am new to using React and FullCalendar, and I have a page layout similar to the image linked below. https://i.sstatic.net/MooTR.png Additionally, I have a list of events structured as shown: id: "9", eventId: "1", ...

Unlock Buffer - JavaScript

I'm working with a simple JavaScript code snippet. let str = "Hello World"; console.log(Buffer.from(str,"utf-8")); The output is: <Buffer 48 65 6c 6c 6f 20 57 6f 72 6c 64> Is there a way to extract the bytes from the Buffe ...

I'm currently working on building a fresh window with Tauri 1.2, combining the powers of Rust, React, and Typescript. However, I've encountered a few

Utilizing Tauri's WindowBuilder in Rust, I attempted to create a new window. Despite successfully generating a blank window, I encountered challenges: The inability to display any content on the window The failure to close the window Being relativel ...

Cosmic - Ways to incorporate personalized validation strategies into the `getConfigValue()` function?

Why is the getConfigValue() function not retrieving validation values from custom Strategies? For example: @Injectable() export class CustomStrategy extends NbPasswordAuthStrategy { protected defaultOptions: CustomStrategyOptions = CustomStrategyOptio ...

Using custom properties from the Material-UI theme object with custom props in Emotion styled components: a step-by-step guide

I have implemented a custom object called fTokens into the MUI theme using Module augmentation in TypeScript This is how my theme.d.ts file is structured declare module "@mui/material/styles" { interface FPalette { ... } interface FTokens ...

Leverage Prisma's auto-generated types as the input type for functions

Exploring the capabilities of Prisma ORM has led me to experiment with creating models and generating the PrismaClient. Initially, I thought it would be possible to utilize the generated types for variables and response types, but that doesn't seem to ...

React/Typescript/VScode - a '.tsx' extension cannot be used at the end of an import path

I have successfully converted a series of React projects to TypeScript, but I am encountering a specific issue with one non-webpack project. The error I am facing is 'an import path cannot end with a .tsx extension'. For example, this error occur ...

crafting connections in 3D using TypeORM (ORM)

I attempted to construct a database schema involving users, groups, documents, and permissions. Users can be part of multiple groups Groups can have multiple users Users can possess permissions for documents Groups can have permissions for documents Perm ...

How to Generate a JPG File from a Leaflet Map in Angular 4 using Typescript

I am developing a custom application using Angular4 that involves integrating leaflet maps. One of the requirements is to export the current view of a map as a JPG image, capturing only the map with markers and polylines - similar to taking a screenshot. ...

The validation through class-validator or class-transformer fails to function properly when applying multiple Types to the @Query decorator

Is there a way to combine multiple types with the @Query() decorator in my controller, such as ParamsWithRegex and PaginationParams? I'm facing an issue where no validation is being applied when I do that. How can I resolve this problem? **// MY CON ...

Exploring the benefits of useContext in Expo router

Currently, I am working with the latest Expo-Router version which incorporates file-based navigation. To establish a universal language context in my application, I have utilized React's context API along with the useReducer hook. However, I am encoun ...

The use of custom loaders alongside ts-node allows for more flexibility

Is it possible to utilize ts-node with a custom loader? The documentation only mentions enabling esm compatibility. ts-node --esm my-file.ts I am attempting to implement a custom loader for testing an ESM module, but I prefer not to rely on node for compi ...

Tips for arranging various information into a unified column within an Antd Table

Is there a way to display multiple data elements in a single cell of an Ant Design table, as it currently only allows insertion of one data element? I am attempting to combine both the 'transactionType' and 'sourceNo' into a single cell ...

Guide to implementing a specified directive value across various tags in Angular Material

As I delve into learning Angular and Material, I have come across a challenge in my project. I noticed that I need to create forms with a consistent appearance. Take for example the registration form's template snippet below: <mat-card> <h2 ...

The functionality of arguments in the whenAllDone promise/deferred javascript helper seems to fail when attempting to encapsulate existing code within a Deferred

My goal is to implement the solution provided in TypeScript from Stack Overflow: UPDATE 2 - The issue with the original answer is that it does not support a single deferred. I have made modifications to reproduce the error in his fiddle. http://jsfiddle.n ...

Steps for utilizing response data as parameters for useInfiniteQueryHere is how you can make

On the specific page where I am implementing useInfiniteQuery const { data, error, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage, status } = useInfiniteQuery( ['posts', searchState], ({ pageParam = 1 }) => post.search({ . ...

Can you explain the distinction between any[] and [] in TypeScript?

Here is an example that successfully works: protected createGroups(sortedItems: Array<TbpeItem>): any[] { let groups: any[] = []; return groups; } However, the second example encounters a TypeScript error: type any[] not assignable to ...

Tips for conducting key down event testing on a material ui MenuList element utilizing react-testing-library

Looking to test the key down event on my MenuList component. Component: import MenuItem from '@material-ui/core/MenuItem'; import MenuList from '@material-ui/core/MenuList'; import * as React from 'react'; export default fu ...