@@ -11,7 +11,31 @@ type MessageIds =
11
11
| 'unsafeArraySpread'
12
12
| 'unsafeSpread' ;
13
13
14
+ const enum RestTypeKind {
15
+ Array ,
16
+ Tuple ,
17
+ Other ,
18
+ }
19
+ type RestType =
20
+ | {
21
+ type : ts . Type ;
22
+ kind : RestTypeKind . Array ;
23
+ index : number ;
24
+ }
25
+ | {
26
+ typeArguments : readonly ts . Type [ ] ;
27
+ kind : RestTypeKind . Tuple ;
28
+ index : number ;
29
+ }
30
+ | {
31
+ type : ts . Type ;
32
+ kind : RestTypeKind . Other ;
33
+ index : number ;
34
+ } ;
35
+
14
36
class FunctionSignature {
37
+ private parameterTypeIndex = 0 ;
38
+
15
39
public static create (
16
40
checker : ts . TypeChecker ,
17
41
tsNode : ts . CallLikeExpression ,
@@ -22,18 +46,34 @@ class FunctionSignature {
22
46
}
23
47
24
48
const paramTypes : ts . Type [ ] = [ ] ;
25
- let restType : ts . Type | null = null ;
49
+ let restType : RestType | null = null ;
26
50
27
- for ( const param of signature . getParameters ( ) ) {
51
+ const parameters = signature . getParameters ( ) ;
52
+ for ( let i = 0 ; i < parameters . length ; i += 1 ) {
53
+ const param = parameters [ i ] ;
28
54
const type = checker . getTypeOfSymbolAtLocation ( param , tsNode ) ;
29
55
30
56
const decl = param . getDeclarations ( ) ?. [ 0 ] ;
31
57
if ( decl && ts . isParameter ( decl ) && decl . dotDotDotToken ) {
32
58
// is a rest param
33
59
if ( checker . isArrayType ( type ) ) {
34
- restType = checker . getTypeArguments ( type ) [ 0 ] ;
60
+ restType = {
61
+ type : checker . getTypeArguments ( type ) [ 0 ] ,
62
+ kind : RestTypeKind . Array ,
63
+ index : i ,
64
+ } ;
65
+ } else if ( checker . isTupleType ( type ) ) {
66
+ restType = {
67
+ typeArguments : checker . getTypeArguments ( type ) ,
68
+ kind : RestTypeKind . Tuple ,
69
+ index : i ,
70
+ } ;
35
71
} else {
36
- restType = type ;
72
+ restType = {
73
+ type,
74
+ kind : RestTypeKind . Other ,
75
+ index : i ,
76
+ } ;
37
77
}
38
78
break ;
39
79
}
@@ -48,12 +88,41 @@ class FunctionSignature {
48
88
49
89
private constructor (
50
90
private paramTypes : ts . Type [ ] ,
51
- private restType : ts . Type | null ,
91
+ private restType : RestType | null ,
52
92
) { }
53
93
54
- public getParameterType ( index : number ) : ts . Type | null {
94
+ public getNextParameterType ( ) : ts . Type | null {
95
+ const index = this . parameterTypeIndex ;
96
+ this . parameterTypeIndex += 1 ;
97
+
55
98
if ( index >= this . paramTypes . length || this . hasConsumedArguments ) {
56
- return this . restType ;
99
+ if ( this . restType == null ) {
100
+ return null ;
101
+ }
102
+
103
+ switch ( this . restType . kind ) {
104
+ case RestTypeKind . Tuple : {
105
+ const typeArguments = this . restType . typeArguments ;
106
+ if ( this . hasConsumedArguments ) {
107
+ // all types consumed by a rest - just assume it's the last type
108
+ // there is one edge case where this is wrong, but we ignore it because
109
+ // it's rare and really complicated to handle
110
+ // eg: function foo(...a: [number, ...string[], number])
111
+ return typeArguments [ typeArguments . length - 1 ] ;
112
+ }
113
+
114
+ const typeIndex = index - this . restType . index ;
115
+ if ( typeIndex >= typeArguments . length ) {
116
+ return typeArguments [ typeArguments . length - 1 ] ;
117
+ }
118
+
119
+ return typeArguments [ typeIndex ] ;
120
+ }
121
+
122
+ case RestTypeKind . Array :
123
+ case RestTypeKind . Other :
124
+ return this . restType . type ;
125
+ }
57
126
}
58
127
return this . paramTypes [ index ] ;
59
128
}
@@ -112,12 +181,7 @@ export default util.createRule<[], MessageIds>({
112
181
return ;
113
182
}
114
183
115
- let parameterTypeIndex = 0 ;
116
- for (
117
- let i = 0 ;
118
- i < node . arguments . length ;
119
- i += 1 , parameterTypeIndex += 1
120
- ) {
184
+ for ( let i = 0 ; i < node . arguments . length ; i += 1 ) {
121
185
const argument = node . arguments [ i ] ;
122
186
123
187
switch ( argument . type ) {
@@ -146,15 +210,9 @@ export default util.createRule<[], MessageIds>({
146
210
const spreadTypeArguments = checker . getTypeArguments (
147
211
spreadArgType ,
148
212
) ;
149
- for (
150
- let j = 0 ;
151
- j < spreadTypeArguments . length ;
152
- j += 1 , parameterTypeIndex += 1
153
- ) {
213
+ for ( let j = 0 ; j < spreadTypeArguments . length ; j += 1 ) {
154
214
const tupleType = spreadTypeArguments [ j ] ;
155
- const parameterType = signature . getParameterType (
156
- parameterTypeIndex ,
157
- ) ;
215
+ const parameterType = signature . getNextParameterType ( ) ;
158
216
if ( parameterType == null ) {
159
217
continue ;
160
218
}
@@ -188,7 +246,7 @@ export default util.createRule<[], MessageIds>({
188
246
}
189
247
190
248
default : {
191
- const parameterType = signature . getParameterType ( i ) ;
249
+ const parameterType = signature . getNextParameterType ( ) ;
192
250
if ( parameterType == null ) {
193
251
continue ;
194
252
}
0 commit comments