@@ -5,15 +5,38 @@ use oxc_ast::{
5
5
} ,
6
6
AstBuilder , Visit ,
7
7
} ;
8
- use oxc_span:: { Atom , GetSpan } ;
8
+ use oxc_span:: { Atom , GetSpan , SPAN } ;
9
9
use oxc_syntax:: scope:: ScopeFlags ;
10
10
11
11
use crate :: { diagnostics:: type_containing_private_name, IsolatedDeclarations } ;
12
12
13
- /// Infer return type from return statement. Does not support multiple return statements.
13
+ /// Infer return type from return statement.
14
+ /// ```ts
15
+ /// function foo() {
16
+ /// return 1;
17
+ /// }
18
+ /// // inferred type is number
19
+ ///
20
+ /// function bar() {
21
+ /// if (true) {
22
+ /// return;
23
+ /// }
24
+ /// return 1;
25
+ /// }
26
+ /// // inferred type is number | undefined
27
+ ///
28
+ /// function baz() {
29
+ /// if (true) {
30
+ /// return null;
31
+ /// }
32
+ /// return 1;
33
+ /// }
34
+ /// // We can't infer return type if there are multiple return statements with different types
35
+ /// ```
36
+ #[ allow( clippy:: option_option) ]
14
37
pub struct FunctionReturnType < ' a > {
15
38
ast : AstBuilder < ' a > ,
16
- return_expression : Option < Expression < ' a > > ,
39
+ return_expression : Option < Option < Expression < ' a > > > ,
17
40
value_bindings : Vec < Atom < ' a > > ,
18
41
type_bindings : Vec < Atom < ' a > > ,
19
42
return_statement_count : u8 ,
@@ -36,48 +59,57 @@ impl<'a> FunctionReturnType<'a> {
36
59
37
60
visitor. visit_function_body ( body) ;
38
61
39
- if visitor. return_statement_count > 1 {
40
- return None ;
41
- }
42
-
43
- visitor. return_expression . and_then ( |expr| {
44
- let expr_type = transformer. infer_type_from_expression ( & expr) ?;
62
+ let expr = visitor. return_expression ??;
63
+ let Some ( mut expr_type) = transformer. infer_type_from_expression ( & expr) else {
64
+ // Avoid report error in parent function
65
+ return if expr. is_function ( ) {
66
+ Some ( transformer. ast . ts_unknown_keyword ( SPAN ) )
67
+ } else {
68
+ None
69
+ } ;
70
+ } ;
45
71
46
- if let Some ( ( reference_name, is_value) ) = match & expr_type {
47
- TSType :: TSTypeReference ( type_reference) => {
48
- if let TSTypeName :: IdentifierReference ( ident) = & type_reference. type_name {
49
- Some ( ( ident. name . clone ( ) , false ) )
50
- } else {
51
- None
52
- }
53
- }
54
- TSType :: TSTypeQuery ( query) => {
55
- if let TSTypeQueryExprName :: IdentifierReference ( ident) = & query. expr_name {
56
- Some ( ( ident. name . clone ( ) , true ) )
57
- } else {
58
- None
59
- }
72
+ if let Some ( ( reference_name, is_value) ) = match & expr_type {
73
+ TSType :: TSTypeReference ( type_reference) => {
74
+ if let TSTypeName :: IdentifierReference ( ident) = & type_reference. type_name {
75
+ Some ( ( ident. name . clone ( ) , false ) )
76
+ } else {
77
+ None
60
78
}
61
- _ => None ,
62
- } {
63
- let is_defined_in_current_scope = if is_value {
64
- visitor . value_bindings . contains ( & reference_name )
79
+ }
80
+ TSType :: TSTypeQuery ( query ) => {
81
+ if let TSTypeQueryExprName :: IdentifierReference ( ident ) = & query . expr_name {
82
+ Some ( ( ident . name . clone ( ) , true ) )
65
83
} else {
66
- visitor. type_bindings . contains ( & reference_name)
67
- } ;
68
-
69
- if is_defined_in_current_scope {
70
- transformer. error ( type_containing_private_name (
71
- & reference_name,
72
- expr_type
73
- . get_identifier_reference ( )
74
- . map_or_else ( || expr_type. span ( ) , |ident| ident. span ) ,
75
- ) ) ;
84
+ None
76
85
}
77
86
}
87
+ _ => None ,
88
+ } {
89
+ let is_defined_in_current_scope = if is_value {
90
+ visitor. value_bindings . contains ( & reference_name)
91
+ } else {
92
+ visitor. type_bindings . contains ( & reference_name)
93
+ } ;
78
94
79
- Some ( expr_type)
80
- } )
95
+ if is_defined_in_current_scope {
96
+ transformer. error ( type_containing_private_name (
97
+ & reference_name,
98
+ expr_type
99
+ . get_identifier_reference ( )
100
+ . map_or_else ( || expr_type. span ( ) , |ident| ident. span ) ,
101
+ ) ) ;
102
+ }
103
+ }
104
+
105
+ //
106
+ if visitor. return_statement_count > 1 {
107
+ let types = transformer
108
+ . ast
109
+ . new_vec_from_iter ( [ expr_type, transformer. ast . ts_undefined_keyword ( SPAN ) ] ) ;
110
+ expr_type = transformer. ast . ts_union_type ( SPAN , types) ;
111
+ }
112
+ Some ( expr_type)
81
113
}
82
114
}
83
115
@@ -109,8 +141,16 @@ impl<'a> Visit<'a> for FunctionReturnType<'a> {
109
141
fn visit_return_statement ( & mut self , stmt : & ReturnStatement < ' a > ) {
110
142
self . return_statement_count += 1 ;
111
143
if self . return_statement_count > 1 {
112
- return ;
144
+ if let Some ( expr) = & self . return_expression {
145
+ // if last return statement is not empty, we can't infer return type
146
+ if expr. is_some ( ) {
147
+ self . return_expression = None ;
148
+ return ;
149
+ }
150
+ } else {
151
+ return ;
152
+ }
113
153
}
114
- self . return_expression = self . ast . copy ( & stmt. argument ) ;
154
+ self . return_expression = Some ( self . ast . copy ( & stmt. argument ) ) ;
115
155
}
116
156
}
0 commit comments