1
1
import type { TSESTree } from '@typescript-eslint/utils' ;
2
2
3
+ import { AST_NODE_TYPES } from '@typescript-eslint/utils' ;
3
4
import * as tsutils from 'ts-api-utils' ;
4
5
import * as ts from 'typescript' ;
5
6
@@ -111,8 +112,12 @@ export default createRule<[], MessageIds>({
111
112
return {
112
113
TSTypeParameterInstantiation ( node ) : void {
113
114
const expression = services . esTreeNodeToTSNodeMap . get ( node ) ;
115
+ const typeParameters = getTypeParametersFromNode (
116
+ node ,
117
+ expression ,
118
+ checker ,
119
+ ) ;
114
120
115
- const typeParameters = getTypeParametersFromNode ( expression , checker ) ;
116
121
if ( typeParameters ) {
117
122
checkTSArgsAndParameters ( node , typeParameters ) ;
118
123
}
@@ -122,29 +127,31 @@ export default createRule<[], MessageIds>({
122
127
} ) ;
123
128
124
129
function getTypeParametersFromNode (
125
- node : ParameterCapableTSNode ,
130
+ node : TSESTree . TSTypeParameterInstantiation ,
131
+ tsNode : ParameterCapableTSNode ,
126
132
checker : ts . TypeChecker ,
127
133
) : readonly ts . TypeParameterDeclaration [ ] | undefined {
128
- if ( ts . isExpressionWithTypeArguments ( node ) ) {
129
- return getTypeParametersFromType ( node . expression , checker ) ;
134
+ if ( ts . isExpressionWithTypeArguments ( tsNode ) ) {
135
+ return getTypeParametersFromType ( node , tsNode . expression , checker ) ;
130
136
}
131
137
132
- if ( ts . isTypeReferenceNode ( node ) ) {
133
- return getTypeParametersFromType ( node . typeName , checker ) ;
138
+ if ( ts . isTypeReferenceNode ( tsNode ) ) {
139
+ return getTypeParametersFromType ( node , tsNode . typeName , checker ) ;
134
140
}
135
141
136
142
if (
137
- ts . isCallExpression ( node ) ||
138
- ts . isNewExpression ( node ) ||
139
- ts . isTaggedTemplateExpression ( node )
143
+ ts . isCallExpression ( tsNode ) ||
144
+ ts . isNewExpression ( tsNode ) ||
145
+ ts . isTaggedTemplateExpression ( tsNode )
140
146
) {
141
- return getTypeParametersFromCall ( node , checker ) ;
147
+ return getTypeParametersFromCall ( node , tsNode , checker ) ;
142
148
}
143
149
144
150
return undefined ;
145
151
}
146
152
147
153
function getTypeParametersFromType (
154
+ node : TSESTree . TSTypeParameterInstantiation ,
148
155
type : ts . ClassDeclaration | ts . EntityName | ts . Expression ,
149
156
checker : ts . TypeChecker ,
150
157
) : readonly ts . TypeParameterDeclaration [ ] | undefined {
@@ -160,24 +167,36 @@ function getTypeParametersFromType(
160
167
return undefined ;
161
168
}
162
169
163
- return findFirstResult ( declarations , decl =>
164
- ts . isClassLike ( decl ) ||
165
- ts . isTypeAliasDeclaration ( decl ) ||
166
- ts . isInterfaceDeclaration ( decl )
167
- ? decl . typeParameters
168
- : undefined ,
170
+ const sortedDeclaraions = sortDeclarationsByTypeValueContext (
171
+ node ,
172
+ declarations ,
169
173
) ;
174
+ return findFirstResult ( sortedDeclaraions , decl => {
175
+ if (
176
+ ts . isTypeAliasDeclaration ( decl ) ||
177
+ ts . isInterfaceDeclaration ( decl ) ||
178
+ ts . isClassLike ( decl )
179
+ ) {
180
+ return decl . typeParameters ;
181
+ }
182
+ if ( ts . isVariableDeclaration ( decl ) ) {
183
+ return getConstructSignatureDeclaration ( symAtLocation , checker )
184
+ ?. typeParameters ;
185
+ }
186
+ return undefined ;
187
+ } ) ;
170
188
}
171
189
172
190
function getTypeParametersFromCall (
173
- node : ts . CallExpression | ts . NewExpression | ts . TaggedTemplateExpression ,
191
+ node : TSESTree . TSTypeParameterInstantiation ,
192
+ tsNode : ts . CallExpression | ts . NewExpression | ts . TaggedTemplateExpression ,
174
193
checker : ts . TypeChecker ,
175
194
) : readonly ts . TypeParameterDeclaration [ ] | undefined {
176
- const sig = checker . getResolvedSignature ( node ) ;
195
+ const sig = checker . getResolvedSignature ( tsNode ) ;
177
196
const sigDecl = sig ?. getDeclaration ( ) ;
178
197
if ( ! sigDecl ) {
179
- return ts . isNewExpression ( node )
180
- ? getTypeParametersFromType ( node . expression , checker )
198
+ return ts . isNewExpression ( tsNode )
199
+ ? getTypeParametersFromType ( node , tsNode . expression , checker )
181
200
: undefined ;
182
201
}
183
202
@@ -192,3 +211,42 @@ function getAliasedSymbol(
192
211
? checker . getAliasedSymbol ( symbol )
193
212
: symbol ;
194
213
}
214
+
215
+ function isInTypeContext ( node : TSESTree . TSTypeParameterInstantiation ) {
216
+ return (
217
+ node . parent . type === AST_NODE_TYPES . TSInterfaceHeritage ||
218
+ node . parent . type === AST_NODE_TYPES . TSTypeReference ||
219
+ node . parent . type === AST_NODE_TYPES . TSClassImplements
220
+ ) ;
221
+ }
222
+
223
+ function isTypeContextDeclaration ( decl : ts . Declaration ) {
224
+ return ts . isTypeAliasDeclaration ( decl ) || ts . isInterfaceDeclaration ( decl ) ;
225
+ }
226
+
227
+ function typeFirstCompare ( declA : ts . Declaration , declB : ts . Declaration ) {
228
+ const aIsType = isTypeContextDeclaration ( declA ) ;
229
+ const bIsType = isTypeContextDeclaration ( declB ) ;
230
+
231
+ return Number ( bIsType ) - Number ( aIsType ) ;
232
+ }
233
+
234
+ function sortDeclarationsByTypeValueContext (
235
+ node : TSESTree . TSTypeParameterInstantiation ,
236
+ declarations : ts . Declaration [ ] ,
237
+ ) {
238
+ const sorted = [ ...declarations ] . sort ( typeFirstCompare ) ;
239
+ if ( isInTypeContext ( node ) ) {
240
+ return sorted ;
241
+ }
242
+ return sorted . reverse ( ) ;
243
+ }
244
+
245
+ function getConstructSignatureDeclaration (
246
+ symbol : ts . Symbol ,
247
+ checker : ts . TypeChecker ,
248
+ ) : ts . SignatureDeclaration | undefined {
249
+ const type = checker . getTypeOfSymbol ( symbol ) ;
250
+ const sig = type . getConstructSignatures ( ) ;
251
+ return sig . at ( 0 ) ?. getDeclaration ( ) ;
252
+ }
0 commit comments