@ttsc/lint 0.5.0-dev.20260429

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,654 @@
1
+ // Bulk implementation of @typescript-eslint rules that work off the
2
+ // AST alone (no checker, no scope analysis). The set is curated to
3
+ // match the rules `eslint-plugin-typescript`'s recommended preset relies
4
+ // on most heavily — the rest of that plugin's catalog is type-aware and
5
+ // out of scope for v0.
6
+ package lint
7
+
8
+ import (
9
+ "strings"
10
+
11
+ shimast "github.com/microsoft/typescript-go/shim/ast"
12
+ )
13
+
14
+ // no-confusing-non-null-assertion: `a! == b` reads ambiguously.
15
+ type noConfusingNonNullAssertion struct{}
16
+
17
+ func (noConfusingNonNullAssertion) Name() string { return "no-confusing-non-null-assertion" }
18
+ func (noConfusingNonNullAssertion) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindBinaryExpression} }
19
+ func (noConfusingNonNullAssertion) Check(ctx *Context, node *shimast.Node) {
20
+ expr := node.AsBinaryExpression()
21
+ if expr == nil || expr.OperatorToken == nil || expr.Left == nil {
22
+ return
23
+ }
24
+ switch expr.OperatorToken.Kind {
25
+ case shimast.KindEqualsEqualsToken,
26
+ shimast.KindEqualsEqualsEqualsToken,
27
+ shimast.KindExclamationEqualsToken,
28
+ shimast.KindExclamationEqualsEqualsToken,
29
+ shimast.KindEqualsToken:
30
+ default:
31
+ return
32
+ }
33
+ if expr.Left.Kind == shimast.KindNonNullExpression {
34
+ ctx.Report(node, "Confusing combination of non-null assertion and equality.")
35
+ }
36
+ }
37
+
38
+ // no-duplicate-enum-values: `enum E { A = 1, B = 1 }` — duplicate values
39
+ // silently collapse.
40
+ type noDuplicateEnumValues struct{}
41
+
42
+ func (noDuplicateEnumValues) Name() string { return "no-duplicate-enum-values" }
43
+ func (noDuplicateEnumValues) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindEnumDeclaration} }
44
+ func (noDuplicateEnumValues) Check(ctx *Context, node *shimast.Node) {
45
+ decl := node.AsEnumDeclaration()
46
+ if decl == nil || decl.Members == nil {
47
+ return
48
+ }
49
+ seen := map[string]bool{}
50
+ for _, member := range decl.Members.Nodes {
51
+ if member == nil {
52
+ continue
53
+ }
54
+ em := member.AsEnumMember()
55
+ if em == nil || em.Initializer == nil {
56
+ continue
57
+ }
58
+ init := em.Initializer
59
+ var key string
60
+ switch init.Kind {
61
+ case shimast.KindNumericLiteral, shimast.KindBigIntLiteral:
62
+ key = "n:" + numericLiteralText(init)
63
+ case shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral:
64
+ key = "s:" + stringLiteralText(init)
65
+ default:
66
+ continue
67
+ }
68
+ if seen[key] {
69
+ ctx.Report(member, "Duplicate enum member value.")
70
+ continue
71
+ }
72
+ seen[key] = true
73
+ }
74
+ }
75
+
76
+ // no-extra-non-null-assertion: `a!!` / `a!?.b` collapses two assertions.
77
+ type noExtraNonNullAssertion struct{}
78
+
79
+ func (noExtraNonNullAssertion) Name() string { return "no-extra-non-null-assertion" }
80
+ func (noExtraNonNullAssertion) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindNonNullExpression} }
81
+ func (noExtraNonNullAssertion) Check(ctx *Context, node *shimast.Node) {
82
+ parent := node.Parent
83
+ if parent == nil {
84
+ return
85
+ }
86
+ if parent.Kind == shimast.KindNonNullExpression {
87
+ ctx.Report(node, "Forbidden extra non-null assertion.")
88
+ }
89
+ }
90
+
91
+ // no-non-null-asserted-optional-chain: `foo?.bar!` — the chain produces
92
+ // undefined; asserting non-null on the whole chain defeats the chain.
93
+ type noNonNullAssertedOptionalChain struct{}
94
+
95
+ func (noNonNullAssertedOptionalChain) Name() string { return "no-non-null-asserted-optional-chain" }
96
+ func (noNonNullAssertedOptionalChain) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindNonNullExpression} }
97
+ func (noNonNullAssertedOptionalChain) Check(ctx *Context, node *shimast.Node) {
98
+ inner := node.AsNonNullExpression()
99
+ if inner == nil || inner.Expression == nil {
100
+ return
101
+ }
102
+ if containsOptionalChain(inner.Expression) {
103
+ ctx.Report(node, "Optional chain expressions can return undefined; non-null assertion bypasses that check.")
104
+ }
105
+ }
106
+
107
+ func containsOptionalChain(node *shimast.Node) bool {
108
+ if node == nil {
109
+ return false
110
+ }
111
+ switch node.Kind {
112
+ case shimast.KindPropertyAccessExpression:
113
+ access := node.AsPropertyAccessExpression()
114
+ if access != nil && access.QuestionDotToken != nil {
115
+ return true
116
+ }
117
+ if access != nil {
118
+ return containsOptionalChain(access.Expression)
119
+ }
120
+ case shimast.KindElementAccessExpression:
121
+ access := node.AsElementAccessExpression()
122
+ if access != nil && access.QuestionDotToken != nil {
123
+ return true
124
+ }
125
+ if access != nil {
126
+ return containsOptionalChain(access.Expression)
127
+ }
128
+ case shimast.KindCallExpression:
129
+ call := node.AsCallExpression()
130
+ if call != nil && call.QuestionDotToken != nil {
131
+ return true
132
+ }
133
+ if call != nil {
134
+ return containsOptionalChain(call.Expression)
135
+ }
136
+ }
137
+ return false
138
+ }
139
+
140
+ // no-misused-new: declaring a `new` signature on a non-class interface
141
+ // or a `constructor` method on an interface — these don't do what
142
+ // authors expect.
143
+ type noMisusedNew struct{}
144
+
145
+ func (noMisusedNew) Name() string { return "no-misused-new" }
146
+ func (noMisusedNew) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindInterfaceDeclaration, shimast.KindTypeAliasDeclaration} }
147
+ func (noMisusedNew) Check(ctx *Context, node *shimast.Node) {
148
+ if node.Kind != shimast.KindInterfaceDeclaration {
149
+ return
150
+ }
151
+ decl := node.AsInterfaceDeclaration()
152
+ if decl == nil || decl.Members == nil {
153
+ return
154
+ }
155
+ for _, member := range decl.Members.Nodes {
156
+ if member == nil {
157
+ continue
158
+ }
159
+ switch member.Kind {
160
+ case shimast.KindConstructor:
161
+ ctx.Report(member, "Interfaces cannot have constructors. Use a class instead.")
162
+ case shimast.KindMethodSignature:
163
+ ms := member.AsMethodSignatureDeclaration()
164
+ if ms != nil && identifierText(ms.Name()) == "constructor" {
165
+ ctx.Report(member, "Interfaces cannot have constructors. Use a class instead.")
166
+ }
167
+ }
168
+ }
169
+ }
170
+
171
+ // prefer-enum-initializers: every enum member should have an explicit
172
+ // initializer (avoids order-dependent values).
173
+ type preferEnumInitializers struct{}
174
+
175
+ func (preferEnumInitializers) Name() string { return "prefer-enum-initializers" }
176
+ func (preferEnumInitializers) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindEnumDeclaration} }
177
+ func (preferEnumInitializers) Check(ctx *Context, node *shimast.Node) {
178
+ decl := node.AsEnumDeclaration()
179
+ if decl == nil || decl.Members == nil {
180
+ return
181
+ }
182
+ for _, member := range decl.Members.Nodes {
183
+ if member == nil {
184
+ continue
185
+ }
186
+ em := member.AsEnumMember()
187
+ if em != nil && em.Initializer == nil {
188
+ ctx.Report(member, "Enum member should have an explicit initializer.")
189
+ }
190
+ }
191
+ }
192
+
193
+ // prefer-for-of: `for (let i = 0; i < arr.length; i++) { use(arr[i]) }`
194
+ // can usually be replaced with `for (const x of arr) { use(x); }`.
195
+ type preferForOf struct{}
196
+
197
+ func (preferForOf) Name() string { return "prefer-for-of" }
198
+ func (preferForOf) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindForStatement} }
199
+ func (preferForOf) Check(ctx *Context, node *shimast.Node) {
200
+ loop := node.AsForStatement()
201
+ if loop == nil || loop.Initializer == nil || loop.Condition == nil || loop.Incrementor == nil {
202
+ return
203
+ }
204
+ // Initializer: `let i = 0` (single declarator with name `i`).
205
+ init := loop.Initializer
206
+ if init.Kind != shimast.KindVariableDeclarationList {
207
+ return
208
+ }
209
+ list := init.AsVariableDeclarationList()
210
+ if list == nil || list.Declarations == nil || len(list.Declarations.Nodes) != 1 {
211
+ return
212
+ }
213
+ decl := list.Declarations.Nodes[0].AsVariableDeclaration()
214
+ if decl == nil {
215
+ return
216
+ }
217
+ counter := identifierText(decl.Name())
218
+ if counter == "" {
219
+ return
220
+ }
221
+ if numericLiteralText(decl.Initializer) != "0" {
222
+ return
223
+ }
224
+ // Condition: `i < <something>.length`.
225
+ cond := loop.Condition.AsBinaryExpression()
226
+ if cond == nil || cond.OperatorToken == nil {
227
+ return
228
+ }
229
+ if cond.OperatorToken.Kind != shimast.KindLessThanToken {
230
+ return
231
+ }
232
+ if identifierText(cond.Left) != counter {
233
+ return
234
+ }
235
+ if cond.Right == nil || cond.Right.Kind != shimast.KindPropertyAccessExpression {
236
+ return
237
+ }
238
+ rightAccess := cond.Right.AsPropertyAccessExpression()
239
+ if rightAccess == nil || identifierText(rightAccess.Name()) != "length" {
240
+ return
241
+ }
242
+ // Incrementor: `i++` or `++i`.
243
+ if !isCounterIncrement(loop.Incrementor, counter) {
244
+ return
245
+ }
246
+ ctx.Report(node, "Prefer a 'for-of' loop instead of a 'for' loop with this simple iteration.")
247
+ }
248
+
249
+ func isCounterIncrement(node *shimast.Node, counter string) bool {
250
+ switch node.Kind {
251
+ case shimast.KindPostfixUnaryExpression:
252
+ post := node.AsPostfixUnaryExpression()
253
+ return post != nil && post.Operator == shimast.KindPlusPlusToken && identifierText(post.Operand) == counter
254
+ case shimast.KindPrefixUnaryExpression:
255
+ pre := node.AsPrefixUnaryExpression()
256
+ return pre != nil && pre.Operator == shimast.KindPlusPlusToken && identifierText(pre.Operand) == counter
257
+ }
258
+ return false
259
+ }
260
+
261
+ // prefer-function-type: a single-call-signature interface or type alias
262
+ // is more readably written as a function type.
263
+ //
264
+ // interface F { (x: number): string } -> type F = (x: number) => string
265
+ type preferFunctionType struct{}
266
+
267
+ func (preferFunctionType) Name() string { return "prefer-function-type" }
268
+ func (preferFunctionType) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindInterfaceDeclaration} }
269
+ func (preferFunctionType) Check(ctx *Context, node *shimast.Node) {
270
+ decl := node.AsInterfaceDeclaration()
271
+ if decl == nil || decl.Members == nil || len(decl.Members.Nodes) != 1 {
272
+ return
273
+ }
274
+ if decl.HeritageClauses != nil && len(decl.HeritageClauses.Nodes) > 0 {
275
+ return
276
+ }
277
+ member := decl.Members.Nodes[0]
278
+ if member == nil || member.Kind != shimast.KindCallSignature {
279
+ return
280
+ }
281
+ ctx.Report(node, "Interface only has a call signature; use 'type' alias and function type instead.")
282
+ }
283
+
284
+ // prefer-namespace-keyword: `module Foo {}` (TS namespace via `module`
285
+ // keyword) → `namespace Foo {}`.
286
+ type preferNamespaceKeyword struct{}
287
+
288
+ func (preferNamespaceKeyword) Name() string { return "prefer-namespace-keyword" }
289
+ func (preferNamespaceKeyword) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindModuleDeclaration} }
290
+ func (preferNamespaceKeyword) Check(ctx *Context, node *shimast.Node) {
291
+ decl := node.AsModuleDeclaration()
292
+ if decl == nil || decl.Name() == nil {
293
+ return
294
+ }
295
+ if decl.Name().Kind == shimast.KindStringLiteral {
296
+ return // ambient module: `declare module "fs" {}` is fine
297
+ }
298
+ if decl.Keyword != shimast.KindModuleKeyword {
299
+ return
300
+ }
301
+ ctx.Report(node, "Use 'namespace' instead of 'module' to declare custom TypeScript modules.")
302
+ }
303
+
304
+ // triple-slash-reference: `/// <reference path="..." />` directives.
305
+ // Discouraged in modern code in favor of `import`.
306
+ type tripleSlashReference struct{}
307
+
308
+ func (tripleSlashReference) Name() string { return "triple-slash-reference" }
309
+ func (tripleSlashReference) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindSourceFile} }
310
+ func (tripleSlashReference) Check(ctx *Context, node *shimast.Node) {
311
+ if ctx.File == nil {
312
+ return
313
+ }
314
+ for _, ref := range ctx.File.ReferencedFiles {
315
+ ctx.ReportRange(ref.Pos(), ref.End(), "Do not use triple slash references for "+ref.FileName+".")
316
+ }
317
+ for _, ref := range ctx.File.TypeReferenceDirectives {
318
+ ctx.ReportRange(ref.Pos(), ref.End(), "Do not use triple slash references for "+ref.FileName+".")
319
+ }
320
+ }
321
+
322
+ // no-array-delete: `delete arr[0]` leaves a sparse hole. Use `splice`.
323
+ type noArrayDelete struct{}
324
+
325
+ func (noArrayDelete) Name() string { return "no-array-delete" }
326
+ func (noArrayDelete) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindDeleteExpression} }
327
+ func (noArrayDelete) Check(ctx *Context, node *shimast.Node) {
328
+ del := node.AsDeleteExpression()
329
+ if del == nil || del.Expression == nil {
330
+ return
331
+ }
332
+ if del.Expression.Kind != shimast.KindElementAccessExpression {
333
+ return
334
+ }
335
+ access := del.Expression.AsElementAccessExpression()
336
+ if access == nil || access.ArgumentExpression == nil {
337
+ return
338
+ }
339
+ // Numeric subscript ⇒ likely-array delete. (Object delete via
340
+ // numeric key is rare.)
341
+ switch access.ArgumentExpression.Kind {
342
+ case shimast.KindNumericLiteral, shimast.KindIdentifier:
343
+ ctx.Report(node, "Using delete with an array expression is unsafe.")
344
+ }
345
+ }
346
+
347
+ // consistent-type-imports: `import { Foo } from "./types"` where Foo is
348
+ // only used as a type → `import type { Foo } from "./types"`. We
349
+ // approximate by flagging every `import type` candidate where the
350
+ // specifier appears in a type-only context inside the file.
351
+ //
352
+ // We use a heuristic: if every reference to an imported name occurs
353
+ // only inside a TypeReferenceNode, flag the import. Falls short on
354
+ // unanalyzable shapes (re-exports, `typeof X`) but matches the most
355
+ // common case.
356
+ type consistentTypeImports struct{}
357
+
358
+ func (consistentTypeImports) Name() string { return "consistent-type-imports" }
359
+ func (consistentTypeImports) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindImportDeclaration} }
360
+ func (consistentTypeImports) Check(ctx *Context, node *shimast.Node) {
361
+ decl := node.AsImportDeclaration()
362
+ if decl == nil || decl.ImportClause == nil {
363
+ return
364
+ }
365
+ clause := decl.ImportClause.AsImportClause()
366
+ if clause == nil {
367
+ return
368
+ }
369
+ if clause.PhaseModifier == shimast.KindTypeKeyword {
370
+ return // already `import type`.
371
+ }
372
+ if clause.NamedBindings == nil || clause.NamedBindings.Kind != shimast.KindNamedImports {
373
+ return
374
+ }
375
+ named := clause.NamedBindings.AsNamedImports()
376
+ if named == nil || named.Elements == nil {
377
+ return
378
+ }
379
+ names := []string{}
380
+ for _, spec := range named.Elements.Nodes {
381
+ if spec == nil {
382
+ continue
383
+ }
384
+ s := spec.AsImportSpecifier()
385
+ if s == nil || s.IsTypeOnly {
386
+ continue
387
+ }
388
+ if name := identifierText(s.Name()); name != "" {
389
+ names = append(names, name)
390
+ }
391
+ }
392
+ if len(names) == 0 {
393
+ return
394
+ }
395
+ if !allUsesAreTypeOnly(ctx.File.AsNode(), names) {
396
+ return
397
+ }
398
+ ctx.Report(node, "All imports in the declaration are only used as types. Use `import type`.")
399
+ }
400
+
401
+ func allUsesAreTypeOnly(root *shimast.Node, names []string) bool {
402
+ want := map[string]bool{}
403
+ for _, n := range names {
404
+ want[n] = true
405
+ }
406
+ allOk := true
407
+ var visit func(n *shimast.Node, inType bool)
408
+ visit = func(n *shimast.Node, inType bool) {
409
+ if n == nil || !allOk {
410
+ return
411
+ }
412
+ typeContext := inType
413
+ switch n.Kind {
414
+ case shimast.KindTypeReference,
415
+ shimast.KindTypeAliasDeclaration,
416
+ shimast.KindInterfaceDeclaration,
417
+ shimast.KindTypeQuery,
418
+ shimast.KindTypeOperator,
419
+ shimast.KindTypeLiteral:
420
+ typeContext = true
421
+ case shimast.KindIdentifier:
422
+ if !typeContext && want[identifierText(n)] {
423
+ allOk = false
424
+ return
425
+ }
426
+ case shimast.KindImportDeclaration:
427
+ return // don't descend into other imports
428
+ }
429
+ n.ForEachChild(func(c *shimast.Node) bool {
430
+ visit(c, typeContext)
431
+ return false
432
+ })
433
+ }
434
+ visit(root, false)
435
+ return allOk
436
+ }
437
+
438
+ // no-empty-object-type: `interface Foo {}` / `type Foo = {}`. ESLint
439
+ // flags empty types because they're equivalent to `unknown` (everything
440
+ // satisfies `{}`).
441
+ type noEmptyObjectType struct{}
442
+
443
+ func (noEmptyObjectType) Name() string { return "no-empty-object-type" }
444
+ func (noEmptyObjectType) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindTypeLiteral} }
445
+ func (noEmptyObjectType) Check(ctx *Context, node *shimast.Node) {
446
+ lit := node.AsTypeLiteralNode()
447
+ if lit == nil || lit.Members == nil {
448
+ return
449
+ }
450
+ if len(lit.Members.Nodes) == 0 {
451
+ ctx.Report(node, "The `{}` type is generally not what's intended; consider `Record<string, unknown>` or `unknown`.")
452
+ }
453
+ }
454
+
455
+ // array-type: `Array<T>` vs `T[]`. ESLint default mode prefers `T[]`.
456
+ type arrayType struct{}
457
+
458
+ func (arrayType) Name() string { return "array-type" }
459
+ func (arrayType) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindTypeReference} }
460
+ func (arrayType) Check(ctx *Context, node *shimast.Node) {
461
+ ref := node.AsTypeReferenceNode()
462
+ if ref == nil || ref.TypeName == nil {
463
+ return
464
+ }
465
+ name := identifierText(ref.TypeName)
466
+ if name != "Array" && name != "ReadonlyArray" {
467
+ return
468
+ }
469
+ if ref.TypeArguments == nil || len(ref.TypeArguments.Nodes) != 1 {
470
+ return
471
+ }
472
+ if name == "Array" {
473
+ ctx.Report(node, "Use 'T[]' instead of 'Array<T>'.")
474
+ } else {
475
+ ctx.Report(node, "Use 'readonly T[]' instead of 'ReadonlyArray<T>'.")
476
+ }
477
+ }
478
+
479
+ // consistent-indexed-object-style: `{ [key: string]: T }` vs
480
+ // `Record<string, T>`. ESLint default prefers `Record`.
481
+ type consistentIndexedObjectStyle struct{}
482
+
483
+ func (consistentIndexedObjectStyle) Name() string { return "consistent-indexed-object-style" }
484
+ func (consistentIndexedObjectStyle) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindTypeLiteral} }
485
+ func (consistentIndexedObjectStyle) Check(ctx *Context, node *shimast.Node) {
486
+ lit := node.AsTypeLiteralNode()
487
+ if lit == nil || lit.Members == nil || len(lit.Members.Nodes) != 1 {
488
+ return
489
+ }
490
+ member := lit.Members.Nodes[0]
491
+ if member == nil || member.Kind != shimast.KindIndexSignature {
492
+ return
493
+ }
494
+ ctx.Report(node, "An index signature is preferred to be a Record type.")
495
+ }
496
+
497
+ // no-explicit-any-rest-parameter — keeping this distinct from
498
+ // no-explicit-any: rest parameters typed `...args: any[]` are common
499
+ // enough that users want to allow them; this rule lets them ban that
500
+ // shape specifically.
501
+ //
502
+ // (Skipped: too narrow / overlaps with no-explicit-any.)
503
+
504
+ // ban-tslint-comment: `// tslint:disable`. tslint is dead; comments
505
+ // referencing it should be cleaned up.
506
+ type banTslintComment struct{}
507
+
508
+ func (banTslintComment) Name() string { return "ban-tslint-comment" }
509
+ func (banTslintComment) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindSourceFile} }
510
+ func (banTslintComment) Check(ctx *Context, node *shimast.Node) {
511
+ if ctx.File == nil {
512
+ return
513
+ }
514
+ text := ctx.File.Text()
515
+ for i := 0; i < len(text)-2; i++ {
516
+ if text[i] == '/' && text[i+1] == '/' {
517
+ // Find end of line.
518
+ end := i
519
+ for end < len(text) && text[end] != '\n' {
520
+ end++
521
+ }
522
+ line := text[i:end]
523
+ if strings.Contains(line, "tslint:") {
524
+ ctx.ReportRange(i, end, "tslint comment detected.")
525
+ }
526
+ i = end
527
+ }
528
+ }
529
+ }
530
+
531
+ // adjacent-overload-signatures: function/method overloads must be
532
+ // declared next to each other. ESLint catches the visual confusion
533
+ // when overloads are interleaved with other members.
534
+ type adjacentOverloadSignatures struct{}
535
+
536
+ func (adjacentOverloadSignatures) Name() string { return "adjacent-overload-signatures" }
537
+ func (adjacentOverloadSignatures) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindInterfaceDeclaration, shimast.KindTypeLiteral, shimast.KindClassDeclaration, shimast.KindClassExpression, shimast.KindModuleBlock, shimast.KindSourceFile} }
538
+ func (adjacentOverloadSignatures) Check(ctx *Context, node *shimast.Node) {
539
+ members := containerMembers(node)
540
+ if len(members) == 0 {
541
+ return
542
+ }
543
+ type entry struct {
544
+ index int
545
+ name string
546
+ kind shimast.Kind
547
+ }
548
+ seen := []entry{}
549
+ for i, m := range members {
550
+ name, kind, ok := overloadName(m)
551
+ if !ok {
552
+ continue
553
+ }
554
+ for _, prev := range seen {
555
+ if prev.name == name && prev.kind == kind && prev.index < i-1 {
556
+ // Check there isn't already a same-name entry adjacent.
557
+ if i > 0 {
558
+ prevName, prevKind, _ := overloadName(members[i-1])
559
+ if prevName == name && prevKind == kind {
560
+ break
561
+ }
562
+ }
563
+ ctx.Report(m, "All "+name+" signatures should be adjacent.")
564
+ break
565
+ }
566
+ }
567
+ seen = append(seen, entry{index: i, name: name, kind: kind})
568
+ }
569
+ }
570
+
571
+ func containerMembers(node *shimast.Node) []*shimast.Node {
572
+ switch node.Kind {
573
+ case shimast.KindInterfaceDeclaration:
574
+ decl := node.AsInterfaceDeclaration()
575
+ if decl != nil && decl.Members != nil {
576
+ return decl.Members.Nodes
577
+ }
578
+ case shimast.KindTypeLiteral:
579
+ lit := node.AsTypeLiteralNode()
580
+ if lit != nil && lit.Members != nil {
581
+ return lit.Members.Nodes
582
+ }
583
+ case shimast.KindClassDeclaration:
584
+ decl := node.AsClassDeclaration()
585
+ if decl != nil && decl.Members != nil {
586
+ return decl.Members.Nodes
587
+ }
588
+ case shimast.KindClassExpression:
589
+ decl := node.AsClassExpression()
590
+ if decl != nil && decl.Members != nil {
591
+ return decl.Members.Nodes
592
+ }
593
+ case shimast.KindModuleBlock:
594
+ mb := node.AsModuleBlock()
595
+ if mb != nil && mb.Statements != nil {
596
+ return mb.Statements.Nodes
597
+ }
598
+ case shimast.KindSourceFile:
599
+ f := node.AsSourceFile()
600
+ if f != nil && f.Statements != nil {
601
+ return f.Statements.Nodes
602
+ }
603
+ }
604
+ return nil
605
+ }
606
+
607
+ func overloadName(m *shimast.Node) (string, shimast.Kind, bool) {
608
+ if m == nil {
609
+ return "", 0, false
610
+ }
611
+ switch m.Kind {
612
+ case shimast.KindMethodSignature:
613
+ ms := m.AsMethodSignatureDeclaration()
614
+ if ms != nil {
615
+ return identifierText(ms.Name()), m.Kind, true
616
+ }
617
+ case shimast.KindMethodDeclaration:
618
+ md := m.AsMethodDeclaration()
619
+ if md != nil {
620
+ return identifierText(md.Name()), m.Kind, true
621
+ }
622
+ case shimast.KindFunctionDeclaration:
623
+ fd := m.AsFunctionDeclaration()
624
+ if fd != nil {
625
+ return identifierText(fd.Name()), m.Kind, true
626
+ }
627
+ case shimast.KindCallSignature, shimast.KindConstructSignature:
628
+ return "(" + m.Kind.String() + ")", m.Kind, true
629
+ }
630
+ return "", 0, false
631
+ }
632
+
633
+ // no-this-alias-helper: shared helpers for ts rules above.
634
+ var _ = struct{}{}
635
+
636
+ func init() {
637
+ Register(noConfusingNonNullAssertion{})
638
+ Register(noDuplicateEnumValues{})
639
+ Register(noExtraNonNullAssertion{})
640
+ Register(noNonNullAssertedOptionalChain{})
641
+ Register(noMisusedNew{})
642
+ Register(preferEnumInitializers{})
643
+ Register(preferForOf{})
644
+ Register(preferFunctionType{})
645
+ Register(preferNamespaceKeyword{})
646
+ Register(tripleSlashReference{})
647
+ Register(noArrayDelete{})
648
+ Register(consistentTypeImports{})
649
+ Register(noEmptyObjectType{})
650
+ Register(arrayType{})
651
+ Register(consistentIndexedObjectStyle{})
652
+ Register(banTslintComment{})
653
+ Register(adjacentOverloadSignatures{})
654
+ }
@@ -0,0 +1,40 @@
1
+ package lint
2
+
3
+ import shimast "github.com/microsoft/typescript-go/shim/ast"
4
+
5
+ // no-var: ban `var` declarations. ESLint canonical:
6
+ // https://eslint.org/docs/latest/rules/no-var
7
+ type noVar struct{}
8
+
9
+ func (noVar) Name() string { return "no-var" }
10
+ func (noVar) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindVariableStatement} }
11
+ func (noVar) Check(ctx *Context, node *shimast.Node) {
12
+ stmt := node.AsVariableStatement()
13
+ if stmt == nil || stmt.DeclarationList == nil {
14
+ return
15
+ }
16
+ if shimast.IsVar(stmt.DeclarationList) {
17
+ ctx.Report(node, "Unexpected var, use let or const instead.")
18
+ }
19
+ }
20
+
21
+ // no-undef-init: forbid `let x = undefined` and `var x = undefined`.
22
+ // ESLint canonical: https://eslint.org/docs/latest/rules/no-undef-init
23
+ type noUndefInit struct{}
24
+
25
+ func (noUndefInit) Name() string { return "no-undef-init" }
26
+ func (noUndefInit) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindVariableDeclaration} }
27
+ func (noUndefInit) Check(ctx *Context, node *shimast.Node) {
28
+ decl := node.AsVariableDeclaration()
29
+ if decl == nil || decl.Initializer == nil {
30
+ return
31
+ }
32
+ if identifierText(decl.Initializer) == "undefined" {
33
+ ctx.Report(decl.Initializer, "It's not necessary to initialize \"undefined\".")
34
+ }
35
+ }
36
+
37
+ func init() {
38
+ Register(noVar{})
39
+ Register(noUndefInit{})
40
+ }