Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type of Generic Stateless Component React? OR Extending generic function interface in typescript to have a further generic?

Problem: The interface of Stateless Functional Component is given as

interface SFC<P = {}> {     (props: P & { children?: ReactNode }, context?: any): ReactElement<any> | null;     propTypes?: ValidationMap<P>; } 

The prop type of my component is also generic as:

interface Prop<V>{     num: V; } 

How to properly define my component? as:

const myCom: <T>SFC<Prop<T>> = <T>(props: Prop<T>)=> <div>test</div> 

gives an error at character 27 that Cannot find name 'T'

Here is :Typescript Playground of modified example

MyFindings:

1:Typescript 2.9.1 support Stateful Generic Component: http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html#generic-type-arguments-in-jsx-elements

class myCom<T> extends React.Component<Prop<T>, any> {    render() {       return <div>test</div>;    } } 

2: Extending SFC to make a new interface as mentioned in following answer would make component's prop type as any: Typescript React stateless function with generic parameter/return types which I don't want. I want to give proper type for my prop

like image 612
Naman Kheterpal Avatar asked Jul 21 '18 20:07

Naman Kheterpal


2 Answers

You can't use generics like this:

const myCom: <T>SFC<Prop<T>> = <T>(props: Prop<T>)=> <div>test</div> 

The TypeScript spec states:

A construct of the form

< T > ( ... ) => { ... } 

could be parsed as an arrow function expression with a type parameter or a type assertion applied to an arrow function with no type parameter.

source; Microsoft/TypeScript spec.md

Your declaration doesn't match the pattern defined in the TypeScript spec, therefore it wont work.

You can however don't use the SFC interface and just declare it yourself.

interface Prop<V> {     num: V; }  // normal function function Abc<T extends string | number>(props: Prop<T>): React.ReactElement<Prop<T>> {     return <div />; }  // const lambda function const Abc: <T extends string | number>(p: Prop<T>) => React.ReactElement<Prop<T>> = (props) => {    return <div /> };  export default function App() {     return (         <React.Fragment>             <Abc<number> num={1} />             <Abc<string> num="abc" />             <Abc<string> num={1} /> // string expected but was number         </React.Fragment>     ); } 
like image 189
jmattheis Avatar answered Sep 19 '22 17:09

jmattheis


There's a pattern to mitigate this issue by declaring generic component type alias outside of component and then simply asserting it when you need it.

Not as pretty, but still reusable and strict.

interface IMyComponentProps<T> {   name: string   type: T }  // instead of inline with component assignment type MyComponentI<T = any> = React.FC<IMyComponentProps<T>>  const MyComponent: MyComponentI = props => <p {...props}>Hello</p>  const TypedComponent = MyComponent as MyComponentI<number> 
like image 24
vadistic Avatar answered Sep 21 '22 17:09

vadistic