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';
class CustomLibraryComponent<P> extends Component<P> {
public render(): JSX.Element {
return <p>Hello world!</p>;
}
}
interface CustomProperties {
text: string;
}
const assignment: ComponentType<CustomProperties> = CustomLibraryComponent; // ERROR
tsconfig.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 executing npm 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:
const assignment: ComponentType<CustomProperties> = CustomLibraryComponent<CustomProperties>;
const assignment: ComponentType<CustomProperties> = CustomLibraryComponent as CustomLibraryComponent<CustomProperties>;
const assignment: ComponentType<CustomProperties> = CustomLibraryComponent as unknown as CustomLibraryComponent<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).
Playground Link
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:
function CustomLibraryComponent<P>(props: Readonly<P>): JSX.Element {
return <p>Hello world!</p>;
}
TypeScript Version: 3.9.3
Search Terms:
Code
tsconfig.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>fromCustomLibraryComponentsolves the problem described here but my actual use case requires the generic parameter, so please ignore thatPis 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
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
anybut I have no idea how. The following alternatives all fail with different errors: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).
Playground Link
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
CustomLibraryComponentis declared as follows: