11import type { ArrowFunctionExpression , ClassMethod , ClassPrivateMethod , Expression , FunctionDeclaration , FunctionExpression , Identifier , ImportDeclaration , MemberExpression , ObjectMethod , Pattern , RestElement , Statement , TSEntityName , TSType , TSTypeAnnotation } from "@babel/types" ;
22import type { NodePath , PluginObj , PluginPass } from "@babel/core" ;
3- import { assignReturnType , assignTypeAnnotation , assignTypeParameters , importName , isTS , nonNullPath } from "./utils.js" ;
3+ import { assignReturnType , assignTypeAnnotation , assignTypeArguments , assignTypeParameters , importName , isTS , nonNullPath } from "./utils.js" ;
44import { AnalysisError , analyzeBody , analyzeHead , ComponentBody , ComponentHead , needsProps , LibRef } from "./analysis.js" ;
55
66type Options = { } ;
@@ -25,10 +25,10 @@ export default function plugin(babel: typeof import("@babel/core")): PluginObj<P
2525 declPath . replaceWithMultiple ( [
2626 t . variableDeclaration ( "const" , [
2727 t . variableDeclarator (
28- ts
28+ typeNode
2929 ? assignTypeAnnotation (
3030 t . cloneNode ( path . node . id ) ,
31- t . tsTypeAnnotation ( typeNode ! ) ,
31+ t . tsTypeAnnotation ( typeNode ) ,
3232 )
3333 : t . cloneNode ( path . node . id ) ,
3434 funcNode ,
@@ -54,10 +54,10 @@ export default function plugin(babel: typeof import("@babel/core")): PluginObj<P
5454 const { funcNode, typeNode } = transformClass ( head , body , { ts } , babel ) ;
5555 path . replaceWith ( t . variableDeclaration ( "const" , [
5656 t . variableDeclarator (
57- ts
57+ typeNode
5858 ? assignTypeAnnotation (
5959 t . cloneNode ( path . node . id ) ,
60- t . tsTypeAnnotation ( typeNode ! ) ,
60+ t . tsTypeAnnotation ( typeNode ) ,
6161 )
6262 : t . cloneNode ( path . node . id ) ,
6363 funcNode ,
@@ -221,7 +221,7 @@ function transformClass(head: ComponentHead, body: ComponentBody, options: { ts:
221221 t . identifier ( field . localSetterName ! ) ,
222222 ] ) ,
223223 ts && field . typeAnnotation ?
224- assignTypeParameters (
224+ assignTypeArguments (
225225 call ,
226226 t . tsTypeParameterInstantiation ( [
227227 field . typeAnnotation . type === "method"
@@ -278,7 +278,7 @@ function transformClass(head: ComponentHead, body: ComponentBody, options: { ts:
278278 [ t . variableDeclarator (
279279 t . identifier ( field . localName ! ) ,
280280 ts && field . typeAnnotation
281- ? assignTypeParameters (
281+ ? assignTypeArguments (
282282 call ,
283283 t . tsTypeParameterInstantiation ( [
284284 field . typeAnnotation . node
@@ -298,7 +298,7 @@ function transformClass(head: ComponentHead, body: ComponentBody, options: { ts:
298298 [ t . variableDeclarator (
299299 t . identifier ( field . localName ! ) ,
300300 ts && field . typeAnnotation
301- ? assignTypeParameters (
301+ ? assignTypeArguments (
302302 call ,
303303 t . tsTypeParameterInstantiation ( [
304304 field . typeAnnotation . node
@@ -311,25 +311,57 @@ function transformClass(head: ComponentHead, body: ComponentBody, options: { ts:
311311 }
312312 const bodyNode = body . render . path . node . body ;
313313 bodyNode . body . splice ( 0 , 0 , ...preamble ) ;
314- const functionNeeded = head . isPure ;
315- const funcNode = functionNeeded
316- ? t . functionExpression (
317- head . name ? t . cloneNode ( head . name ) : undefined ,
318- needsProps ( body ) ? [ t . identifier ( "props" ) ] : [ ] ,
319- bodyNode
320- )
321- : t . arrowFunctionExpression (
322- needsProps ( body ) ? [ t . identifier ( "props" ) ] : [ ] ,
323- bodyNode
324- ) ;
314+ // recast is not smart enough to correctly pretty-print type parameters for arrow functions.
315+ // so we fall back to functions when type parameters are present.
316+ const functionNeeded = head . isPure || ! ! head . typeParameters ;
317+ const params = needsProps ( body )
318+ ? [ assignTypeAnnotation (
319+ t . identifier ( "props" ) ,
320+ // If the function is generic, put type annotations here instead of the `const` to be defined.
321+ // TODO: take children into account, while being careful about difference between `@types/react` v17 and v18
322+ head . typeParameters
323+ ? head . props
324+ ? t . tsTypeAnnotation ( head . props . node )
325+ : undefined
326+ : undefined
327+ ) ]
328+ : [ ] ;
329+ // If the function is generic, put type annotations here instead of the `const` to be defined.
330+ const returnType = head . typeParameters
331+ // Construct `React.ReactElement | null`
332+ ? t . tsTypeAnnotation (
333+ t . tsUnionType ( [
334+ t . tsTypeReference (
335+ toTSEntity ( getReactImport ( "ReactElement" , babel , head . superClassRef ) , babel )
336+ ) ,
337+ t . tsNullKeyword ( ) ,
338+ ] )
339+ )
340+ : undefined ;
341+ const funcNode = assignTypeParameters (
342+ assignReturnType (
343+ functionNeeded
344+ ? t . functionExpression (
345+ head . name ? t . cloneNode ( head . name ) : undefined ,
346+ params ,
347+ bodyNode
348+ )
349+ : t . arrowFunctionExpression (
350+ params ,
351+ bodyNode
352+ ) ,
353+ returnType
354+ ) ,
355+ head . typeParameters ?. node
356+ ) ;
325357 return {
326358 funcNode : head . isPure
327359 ? t . callExpression (
328360 getReactImport ( "memo" , babel , head . superClassRef ) ,
329361 [ funcNode ]
330362 )
331363 : funcNode ,
332- typeNode : ts
364+ typeNode : ts && ! head . typeParameters
333365 ? t . tsTypeReference (
334366 toTSEntity ( getReactImport ( "FC" , babel , head . superClassRef ) , babel ) ,
335367 head . props
0 commit comments