The generic does not validate the types of two properties

type ComponentType = (...args: any) => any;

type PlatformNotificationProps<TIcon extends ComponentType = ComponentType> = {
  component: TIcon;
  arg: Parameters<TIcon>[0];
};

const PlatformNotification = (props: PlatformNotificationProps) => {};

const Icon = (name: string) => '';

const result = PlatformNotification({
  component: Icon,
  arg: 100,
});

For this specific scenario, either the 'arg' value should be a string instead of a number, or the component should accept a number as an argument instead of a string. I was anticipating to receive an error in the console due to this discrepancy, but surprisingly everything is functioning fine.

What would be the proper way to define types for this particular situation?

Answer №1

When you define PlatformNotification as a non-generic function, it won't check types for you.

const PlatformNotification = (props: PlatformNotificationProps) => {};
//    ^?
// const PlatformNotification: (props: PlatformNotificationProps</*default*/ComponentType>) => void

const result = PlatformNotification({
  component: Icon,
// ^?
// (property) component: ComponentType
  arg: 100,
// ^?
// (property) arg: any
});

To get it to work correctly, simply make it generic

const PlatformNotification = <TIcon extends ComponentType>(props: PlatformNotificationProps<TIcon>) => {};

Answer №2

When working with generics, it's important for all types in the chain to pass through the generic arguments.

If you set default values for the arguments in PlatformNotificationProps and then use that type without properly configuring it, TypeScript won't be able to match the arguments from your function with the generic parameters.

One approach to solving this issue is to ensure that every element in your chain is configurable.

type Function<Args extends any[] = any[]> = (...args: Args) => any;

type PlatformNotificationProperties<Arguments extends any[], IconType extends Function<Arguments>> = {
  component: IconType;
  argument: Parameters<IconType>[0];
};

const PlatformNotificationComponent = <Args extends any[] = any[], Cmp extends Function<Args> = Function<Args> >(props: PlatformNotificationProperties<Args, Cmp>) => {};

const CustomIcon = (name: string) => '';

const output = PlatformNotificationComponent({
  component: CustomIcon,
  argument: 100,
});

Answer №3

The main issue here lies in assigning a default generic to PlatformNotificationProps:

type PlatformNotificationProps<TIcon extends ComponentType = ComponentType> = {
...

When you use this type without specifying a specific generic type, TypeScript defaults it to be ComponentType. This is why the variable arg can accept numbers, as it is essentially typed as any:

const result = PlatformNotification({
  component: Icon,
  arg: 100,
// ^? (property) arg: any
});

The same applies to the component:

const result = PlatformNotification({
  component: () => null, // no errors
  arg: 100,
});

To resolve this issue, specify a type when calling PlatformNotificationProps:

const PlatformNotification = (props: PlatformNotificationProps<typeof Icon>) => {};

Now:

const result = PlatformNotification({
  component: Icon,
  arg: 100, // error: type `number` is not assignable to `string`
});

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

Incorrect date generated by Moment.js from Unix timestamp

Is there a way to store unixtime as a Moment.moment state? Using moment(timestamp) seems to provide a different date. const [date, setDate] = useState<moment.Moment | null>(null); const timestamp = Math.floor(date.getTime() / 1000); setDate(m ...

Creating XML templates in Angular 7: A comprehensive guide

How do I pass XML values in Angular 7 when the API requires this specific format of XML code? -modifydata "<datasets><dataset select=\""always\""> <replace match=\""Letter/@FName\"" value=\""Nazeeeeeeeeeeeeer\" ...

How can we exclude fields from JSON.stringify in type-graphql entities?

Utilizing https://github.com/MichalLytek/type-graphql for crafting our graphql schema has posed a challenge. When we serialize the TypeScript entity object, it does not adhere to the field annotations in our GQL entities, resulting in unwanted data leakage ...

Focusing on the specific properties of a type that serve as index signatures

Currently, I am in the process of developing a type definition set that functions on a user-provided type representing the model of their "state". One crucial task I must accomplish is narrowing down the types of their model as I generate new types that w ...

Can you identify the type of component that is returned from the withStyles() function?

My project includes a simple Dictionary component with basic properties: interface DictionaryProps { word: string; } In another component's props, I am looking for a generic component that only requires a string word: dictionary: React.ComponentC ...

How do I resolve validation function error messages in Vuetify?

When utilizing validation methods in Vuetify, I encountered the following error message↓ I simply want to create a form validation check and implement a function that triggers the validation when the 'submit' button is clicked. I believe my i ...

Unpacking Objects in JavaScript and TypeScript: The Power of Destructuring

I have a variable called props. The type includes VariantTheme, VariantSize, VariantGradient, and React.DOMAttributes<HTMLOrSVGElement> Now I need to create another variable, let's name it htmlProps. I want to transfer the values from props to ...

What is the best way to adjust the layout of these two elements using CSS in order to display them on

I need assistance with adjusting the layout of a dropdown list next to its label in an Angular html page. <div *ngIf="this.userRole == 'myrequests'" class="col-2" [ngClass]="{ 'd-none': view != 'list&apo ...

Guide on importing a markdown file (.md) into a TypeScript project

I've been attempting to import readme files in TypeScript, but I keep encountering the error message "module not found." Here is my TypeScript code: import * as readme from "./README.md"; // I'm receiving an error saying module not found I als ...

Bringing in TypeScript declarations for the compiled JavaScript librarybundle

I have a custom library written in TypeScript with multiple files and an index.ts file that handles all the exports. To consolidate the library, I used webpack to compile it into a single index.js file but now I'm facing challenges importing it with ...

Uncovering redundant fields in TypeScript and detecting errors through type inference

Encountering an unusual edge case with the TS compiler regarding type inference. Surprisingly, the code snippet below (with commented lines intact) should trigger a compile error, but it doesn't. interface IReturned { theField?: string; } interfa ...

Using TypeScript with Visual Studio Code to reference a renamed file

I recently changed the name of a file from FooBar.ts to fooBar.ts. Despite updating the file name, VS Code continues to refer back to the old file. In order to resolve this issue, I disabled forceConsistentCasingInFileNames in the tsconfig.json file. Howev ...

What is a creative way to design a mat-radio-group without traditional radio buttons?

I am looking to create a component that offers users a list of selections with the ability to make only one choice at a time. The mat-radio-group functionality seems to be the best fit for this, but I prefer not to display the actual radio button next to t ...

insert information into a fixed-size array using JavaScript

I am attempting to use array.push within a for loop in my TypeScript code: var rows = [ { id: '1', category: 'Snow', value: 'Jon', cheapSource: '35', cheapPrice: '35', amazonSource ...

Acquire request data prior to exiting function in React

I am working on a NextJS application that utilizes axios for making requests to a backend API, which requires an authentication token. To handle this, I have implemented a function that retrieves the auth token and stores it in a variable at the module-lev ...

Creating personalized breakpoints in Material UI using TypeScript

When using the createMuiTheme() function, you have the ability to update breakpoint values like this. const theme = createMuiTheme({ breakpoints: { values: { xs: 0, sm: 600, md: 960, lg: 1280, xl: 1920, }, }, }) ...

Struggling to launch on Vercel and encountering the error message, """is not allowed by Access-Control-Allow-Origin. Status code: 204""

Greetings! I trust you are doing well. Currently, I am engrossed in developing a full-stack application. The app runs smoothly on localhost without any issues. However, upon deploying both the server and front end on Vercel, a snag arose when attempting to ...

Attempting to grasp the concept of implementing FormArray

When I execute the following code: this.formArray = new FormArray([ new FormControl(''), new FormControl(''), ]); formControlA() { return this.formArray.at(0); } formControlB() { return this.formArray.at(1); } and then use it ...

The ngx-material-timepicker lacks proper design when the minutesGap is set to 1

https://i.sstatic.net/J4z5H.png Check out this example of HTML code I have written: <mat-form-field> <mat-label>StartTime</mat-label> <input matInput readonly [ngxTimepicker]="timeStart" [formControlName]="'sta ...

Which Index Type is the best fit for my assignment?

Color, by default, is a string that is set to primary. However, when used as an index in the Colors array, I encounter an issue where it is recognized as an any type. This happens because a string cannot be used as an index on type '{..etc}' The ...