- Notifications
You must be signed in to change notification settings - Fork 13.2k
Description
TypeScript Version: 3.9.3
Search Terms:
- ComponentType
- React generic component
- Higher-order component (HOC)
- Assignment of intersections
- TS2322, "is not assignable to type"
Code
import{Component,ComponentType,createElement}from'react';classCustomLibraryComponent<P>extendsComponent<P>{publicrender(): JSX.Element{return<p>Helloworld!</p>;}}interfaceCustomProperties{text: string;}constassignment: ComponentType<CustomProperties>=CustomLibraryComponent;// ERRORtsconfig.json
{"compilerOptions":{"jsx": "react", "jsxFactory": "createElement", "moduleResolution": "node", "target": "ESNext", }, } package.json
{"private": true, "scripts":{"compile": "tsc --project tsconfig.json" }, "devDependencies":{"@types/react": "16.9.35", "typescript": "3.9.3" } } Additional explanations
The error can be reproduced by executingnpm run compile. The example is silly, of course, but it's the most compact way to demonstrate the problem. I want to be able to decorate a class-based component with a generic type parameter. (I want to use my custom library component in different places with different type instantiations, thus removing <P> from CustomLibraryComponent solves the problem described here but my actual use case requires the generic parameter, so please ignore that P is not actually used in the example above.) And I'm not interested in the assignment per se but the following, more realistic code fails due to the same reason: function decorator(WrappedComponent: ComponentType<CustomProperties>){return class HOC extends Component{public render(): JSX.Element{return <WrappedComponent text="Example" />} }} const result = decorator(CustomLibraryComponent); In my actual use case, the decorator itself is also generic but this just adds unnecessary detail and complexity here. Expected behavior: Either TS 3.9.3 should still not complain or give me a way to fix the issue.
Actual behavior: Compiling the above code results in
error TS2322: Type 'typeof CustomLibraryComponent' is not assignable to type 'ComponentType<CustomProperties>'. Type 'typeof CustomLibraryComponent' is not assignable to type 'ComponentClass<CustomProperties, any>'. Construct signature return types 'CustomLibraryComponent<any>' and 'Component<CustomProperties, any, any>' are incompatible. The types of 'props' are incompatible between these types. Type 'Readonly<any> & Readonly<{children?: ReactNode}>' is not assignable to type 'Readonly<CustomProperties> & Readonly<{children?: ReactNode}>'. Property 'text' is missing in type 'Readonly<any> & Readonly<{children?: ReactNode}>' but required in type 'Readonly<CustomProperties>'. const assignment: ComponentType<CustomProperties> = CustomLibraryComponent; It's not clear to me whether this is really a problem with TypeScript, actually. The reason why I decided to report this issue here (rather than at DefinitelyTyped or simply asking on StackOverflow) is because it isn't obvious to me why TypeScript complains about Type 'Readonly<any> & Readonly<{children?: ReactNode}>' is not assignable to type 'Readonly<CustomProperties> & Readonly<{children?: ReactNode}>'. Additionally, it's clearly TypeScript that introduced the error. Using "typescript": "3.8.3", the above code works fine.
An alternative to permitting this assignment again would be to somehow tell TypeScript the type to use instead of any but I have no idea how. The following alternatives all fail with different errors:
constassignment: ComponentType<CustomProperties>=CustomLibraryComponent<CustomProperties>;constassignment: ComponentType<CustomProperties>=CustomLibraryComponentasCustomLibraryComponent<CustomProperties>;constassignment: ComponentType<CustomProperties>=CustomLibraryComponentasunknownasCustomLibraryComponent<CustomProperties>;If the error could be fixed by improving the typing of React, please let me know (ideally with a suggestion how to fix it there).
Related Issues: I assume the error has been introduced with the pull request #37195. The only other regression I found in this regard is #38542.
Workaround: Interestingly, generic functional components still work. The above code compiles with TypeScript 3.9.3 when the CustomLibraryComponent is declared as follows:
functionCustomLibraryComponent<P>(props: Readonly<P>): JSX.Element{return<p>Helloworld!</p>;}