@ttsc/lint 0.7.2 → 0.8.0-dev.20260505

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.
@@ -9,23 +9,23 @@ type noThrowLiteral struct{}
9
9
  func (noThrowLiteral) Name() string { return "no-throw-literal" }
10
10
  func (noThrowLiteral) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindThrowStatement} }
11
11
  func (noThrowLiteral) Check(ctx *Context, node *shimast.Node) {
12
- throw := node.AsThrowStatement()
13
- if throw == nil {
14
- return
15
- }
16
- expr := stripParens(throw.Expression)
17
- if expr == nil {
18
- return
19
- }
20
- if isLiteralExpression(expr) || expr.Kind == shimast.KindUndefinedKeyword {
21
- ctx.Report(throw.Expression, "Expected an error object to be thrown.")
22
- return
23
- }
24
- if id := identifierText(expr); id == "undefined" {
25
- ctx.Report(throw.Expression, "Expected an error object to be thrown.")
26
- }
12
+ throw := node.AsThrowStatement()
13
+ if throw == nil {
14
+ return
15
+ }
16
+ expr := stripParens(throw.Expression)
17
+ if expr == nil {
18
+ return
19
+ }
20
+ if isLiteralExpression(expr) || expr.Kind == shimast.KindUndefinedKeyword {
21
+ ctx.Report(throw.Expression, "Expected an error object to be thrown.")
22
+ return
23
+ }
24
+ if id := identifierText(expr); id == "undefined" {
25
+ ctx.Report(throw.Expression, "Expected an error object to be thrown.")
26
+ }
27
27
  }
28
28
 
29
29
  func init() {
30
- Register(noThrowLiteral{})
30
+ Register(noThrowLiteral{})
31
31
  }
@@ -9,7 +9,7 @@ type noExplicitAny struct{}
9
9
  func (noExplicitAny) Name() string { return "no-explicit-any" }
10
10
  func (noExplicitAny) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindAnyKeyword} }
11
11
  func (noExplicitAny) Check(ctx *Context, node *shimast.Node) {
12
- ctx.Report(node, "Unexpected any. Specify a different type.")
12
+ ctx.Report(node, "Unexpected any. Specify a different type.")
13
13
  }
14
14
 
15
15
  // no-non-null-assertion: ban the postfix `!` non-null assertion.
@@ -17,10 +17,10 @@ type noNonNullAssertion struct{}
17
17
 
18
18
  func (noNonNullAssertion) Name() string { return "no-non-null-assertion" }
19
19
  func (noNonNullAssertion) Visits() []shimast.Kind {
20
- return []shimast.Kind{shimast.KindNonNullExpression}
20
+ return []shimast.Kind{shimast.KindNonNullExpression}
21
21
  }
22
22
  func (noNonNullAssertion) Check(ctx *Context, node *shimast.Node) {
23
- ctx.Report(node, "Forbidden non-null assertion.")
23
+ ctx.Report(node, "Forbidden non-null assertion.")
24
24
  }
25
25
 
26
26
  // no-empty-interface: empty `interface { }` declarations are an alias
@@ -29,16 +29,16 @@ type noEmptyInterface struct{}
29
29
 
30
30
  func (noEmptyInterface) Name() string { return "no-empty-interface" }
31
31
  func (noEmptyInterface) Visits() []shimast.Kind {
32
- return []shimast.Kind{shimast.KindInterfaceDeclaration}
32
+ return []shimast.Kind{shimast.KindInterfaceDeclaration}
33
33
  }
34
34
  func (noEmptyInterface) Check(ctx *Context, node *shimast.Node) {
35
- decl := node.AsInterfaceDeclaration()
36
- if decl == nil || decl.Members == nil {
37
- return
38
- }
39
- if len(decl.Members.Nodes) == 0 {
40
- ctx.Report(node, "An empty interface is equivalent to '{}'.")
41
- }
35
+ decl := node.AsInterfaceDeclaration()
36
+ if decl == nil || decl.Members == nil {
37
+ return
38
+ }
39
+ if len(decl.Members.Nodes) == 0 {
40
+ ctx.Report(node, "An empty interface is equivalent to '{}'.")
41
+ }
42
42
  }
43
43
 
44
44
  // no-inferrable-types: `let x: number = 0` — the annotation is what TS
@@ -47,73 +47,73 @@ type noInferrableTypes struct{}
47
47
 
48
48
  func (noInferrableTypes) Name() string { return "no-inferrable-types" }
49
49
  func (noInferrableTypes) Visits() []shimast.Kind {
50
- return []shimast.Kind{shimast.KindVariableDeclaration, shimast.KindParameter, shimast.KindPropertyDeclaration}
50
+ return []shimast.Kind{shimast.KindVariableDeclaration, shimast.KindParameter, shimast.KindPropertyDeclaration}
51
51
  }
52
52
  func (noInferrableTypes) Check(ctx *Context, node *shimast.Node) {
53
- var typeNode, init *shimast.Node
54
- switch node.Kind {
55
- case shimast.KindVariableDeclaration:
56
- decl := node.AsVariableDeclaration()
57
- if decl == nil {
58
- return
59
- }
60
- typeNode = decl.Type
61
- init = decl.Initializer
62
- case shimast.KindParameter:
63
- decl := node.AsParameterDeclaration()
64
- if decl == nil {
65
- return
66
- }
67
- typeNode = decl.Type
68
- init = decl.Initializer
69
- case shimast.KindPropertyDeclaration:
70
- decl := node.AsPropertyDeclaration()
71
- if decl == nil {
72
- return
73
- }
74
- typeNode = decl.Type
75
- init = decl.Initializer
76
- }
77
- if typeNode == nil || init == nil {
78
- return
79
- }
80
- if !isInferrablePair(typeNode, init) {
81
- return
82
- }
83
- ctx.Report(typeNode, "Type annotation here is unnecessary.")
53
+ var typeNode, init *shimast.Node
54
+ switch node.Kind {
55
+ case shimast.KindVariableDeclaration:
56
+ decl := node.AsVariableDeclaration()
57
+ if decl == nil {
58
+ return
59
+ }
60
+ typeNode = decl.Type
61
+ init = decl.Initializer
62
+ case shimast.KindParameter:
63
+ decl := node.AsParameterDeclaration()
64
+ if decl == nil {
65
+ return
66
+ }
67
+ typeNode = decl.Type
68
+ init = decl.Initializer
69
+ case shimast.KindPropertyDeclaration:
70
+ decl := node.AsPropertyDeclaration()
71
+ if decl == nil {
72
+ return
73
+ }
74
+ typeNode = decl.Type
75
+ init = decl.Initializer
76
+ }
77
+ if typeNode == nil || init == nil {
78
+ return
79
+ }
80
+ if !isInferrablePair(typeNode, init) {
81
+ return
82
+ }
83
+ ctx.Report(typeNode, "Type annotation here is unnecessary.")
84
84
  }
85
85
 
86
86
  func isInferrablePair(typeNode, init *shimast.Node) bool {
87
- switch typeNode.Kind {
88
- case shimast.KindStringKeyword:
89
- return init.Kind == shimast.KindStringLiteral || init.Kind == shimast.KindNoSubstitutionTemplateLiteral || init.Kind == shimast.KindTemplateExpression
90
- case shimast.KindNumberKeyword:
91
- return init.Kind == shimast.KindNumericLiteral || isUnaryNumeric(init)
92
- case shimast.KindBooleanKeyword:
93
- return init.Kind == shimast.KindTrueKeyword || init.Kind == shimast.KindFalseKeyword
94
- case shimast.KindBigIntKeyword:
95
- return init.Kind == shimast.KindBigIntLiteral
96
- case shimast.KindNullKeyword:
97
- return init.Kind == shimast.KindNullKeyword
98
- case shimast.KindUndefinedKeyword:
99
- return identifierText(init) == "undefined" || init.Kind == shimast.KindVoidExpression
100
- }
101
- return false
87
+ switch typeNode.Kind {
88
+ case shimast.KindStringKeyword:
89
+ return init.Kind == shimast.KindStringLiteral || init.Kind == shimast.KindNoSubstitutionTemplateLiteral || init.Kind == shimast.KindTemplateExpression
90
+ case shimast.KindNumberKeyword:
91
+ return init.Kind == shimast.KindNumericLiteral || isUnaryNumeric(init)
92
+ case shimast.KindBooleanKeyword:
93
+ return init.Kind == shimast.KindTrueKeyword || init.Kind == shimast.KindFalseKeyword
94
+ case shimast.KindBigIntKeyword:
95
+ return init.Kind == shimast.KindBigIntLiteral
96
+ case shimast.KindNullKeyword:
97
+ return init.Kind == shimast.KindNullKeyword
98
+ case shimast.KindUndefinedKeyword:
99
+ return identifierText(init) == "undefined" || init.Kind == shimast.KindVoidExpression
100
+ }
101
+ return false
102
102
  }
103
103
 
104
104
  func isUnaryNumeric(node *shimast.Node) bool {
105
- if node == nil || node.Kind != shimast.KindPrefixUnaryExpression {
106
- return false
107
- }
108
- prefix := node.AsPrefixUnaryExpression()
109
- if prefix == nil {
110
- return false
111
- }
112
- switch prefix.Operator {
113
- case shimast.KindPlusToken, shimast.KindMinusToken:
114
- return prefix.Operand != nil && prefix.Operand.Kind == shimast.KindNumericLiteral
115
- }
116
- return false
105
+ if node == nil || node.Kind != shimast.KindPrefixUnaryExpression {
106
+ return false
107
+ }
108
+ prefix := node.AsPrefixUnaryExpression()
109
+ if prefix == nil {
110
+ return false
111
+ }
112
+ switch prefix.Operator {
113
+ case shimast.KindPlusToken, shimast.KindMinusToken:
114
+ return prefix.Operand != nil && prefix.Operand.Kind == shimast.KindNumericLiteral
115
+ }
116
+ return false
117
117
  }
118
118
 
119
119
  // no-namespace: TypeScript-only `namespace`/`module` declarations. They
@@ -123,16 +123,16 @@ type noNamespace struct{}
123
123
  func (noNamespace) Name() string { return "no-namespace" }
124
124
  func (noNamespace) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindModuleDeclaration} }
125
125
  func (noNamespace) Check(ctx *Context, node *shimast.Node) {
126
- decl := node.AsModuleDeclaration()
127
- if decl == nil || decl.Name() == nil {
128
- return
129
- }
130
- // Skip `declare module "fs"` ambient module declarations — those are
131
- // the legitimate use case.
132
- if decl.Name().Kind == shimast.KindStringLiteral {
133
- return
134
- }
135
- ctx.Report(node, "ES2015 module syntax is preferred over namespaces.")
126
+ decl := node.AsModuleDeclaration()
127
+ if decl == nil || decl.Name() == nil {
128
+ return
129
+ }
130
+ // Skip `declare module "fs"` ambient module declarations — those are
131
+ // the legitimate use case.
132
+ if decl.Name().Kind == shimast.KindStringLiteral {
133
+ return
134
+ }
135
+ ctx.Report(node, "ES2015 module syntax is preferred over namespaces.")
136
136
  }
137
137
 
138
138
  // no-this-alias: `const self = this;` reassigns `this` to a local. Use
@@ -142,13 +142,13 @@ type noThisAlias struct{}
142
142
  func (noThisAlias) Name() string { return "no-this-alias" }
143
143
  func (noThisAlias) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindVariableDeclaration} }
144
144
  func (noThisAlias) Check(ctx *Context, node *shimast.Node) {
145
- decl := node.AsVariableDeclaration()
146
- if decl == nil || decl.Initializer == nil {
147
- return
148
- }
149
- if decl.Initializer.Kind == shimast.KindThisKeyword {
150
- ctx.Report(node, "Unexpected aliasing of 'this' to local variable.")
151
- }
145
+ decl := node.AsVariableDeclaration()
146
+ if decl == nil || decl.Initializer == nil {
147
+ return
148
+ }
149
+ if decl.Initializer.Kind == shimast.KindThisKeyword {
150
+ ctx.Report(node, "Unexpected aliasing of 'this' to local variable.")
151
+ }
152
152
  }
153
153
 
154
154
  // prefer-as-const: `as 'foo'` / `as 1` should be `as const`.
@@ -156,50 +156,50 @@ type preferAsConst struct{}
156
156
 
157
157
  func (preferAsConst) Name() string { return "prefer-as-const" }
158
158
  func (preferAsConst) Visits() []shimast.Kind {
159
- return []shimast.Kind{shimast.KindAsExpression, shimast.KindTypeAssertionExpression}
159
+ return []shimast.Kind{shimast.KindAsExpression, shimast.KindTypeAssertionExpression}
160
160
  }
161
161
  func (preferAsConst) Check(ctx *Context, node *shimast.Node) {
162
- var expr, typeNode *shimast.Node
163
- switch node.Kind {
164
- case shimast.KindAsExpression:
165
- as := node.AsAsExpression()
166
- if as == nil {
167
- return
168
- }
169
- expr = as.Expression
170
- typeNode = as.Type
171
- case shimast.KindTypeAssertionExpression:
172
- ta := node.AsTypeAssertion()
173
- if ta == nil {
174
- return
175
- }
176
- expr = ta.Expression
177
- typeNode = ta.Type
178
- }
179
- if expr == nil || typeNode == nil {
180
- return
181
- }
182
- if typeNode.Kind != shimast.KindLiteralType {
183
- return
184
- }
185
- literalType := typeNode.AsLiteralTypeNode()
186
- if literalType == nil || literalType.Literal == nil {
187
- return
188
- }
189
- if !literalsMatchSourceText(ctx.File, expr, literalType.Literal) {
190
- return
191
- }
192
- ctx.Report(node, "Expected `as const` instead of `as` literal type.")
162
+ var expr, typeNode *shimast.Node
163
+ switch node.Kind {
164
+ case shimast.KindAsExpression:
165
+ as := node.AsAsExpression()
166
+ if as == nil {
167
+ return
168
+ }
169
+ expr = as.Expression
170
+ typeNode = as.Type
171
+ case shimast.KindTypeAssertionExpression:
172
+ ta := node.AsTypeAssertion()
173
+ if ta == nil {
174
+ return
175
+ }
176
+ expr = ta.Expression
177
+ typeNode = ta.Type
178
+ }
179
+ if expr == nil || typeNode == nil {
180
+ return
181
+ }
182
+ if typeNode.Kind != shimast.KindLiteralType {
183
+ return
184
+ }
185
+ literalType := typeNode.AsLiteralTypeNode()
186
+ if literalType == nil || literalType.Literal == nil {
187
+ return
188
+ }
189
+ if !literalsMatchSourceText(ctx.File, expr, literalType.Literal) {
190
+ return
191
+ }
192
+ ctx.Report(node, "Expected `as const` instead of `as` literal type.")
193
193
  }
194
194
 
195
195
  func literalsMatchSourceText(file *shimast.SourceFile, lhs, rhs *shimast.Node) bool {
196
- if lhs == nil || rhs == nil {
197
- return false
198
- }
199
- if !isLiteralExpression(lhs) {
200
- return false
201
- }
202
- return nodeText(file, lhs) == nodeText(file, rhs)
196
+ if lhs == nil || rhs == nil {
197
+ return false
198
+ }
199
+ if !isLiteralExpression(lhs) {
200
+ return false
201
+ }
202
+ return nodeText(file, lhs) == nodeText(file, rhs)
203
203
  }
204
204
 
205
205
  // no-require-imports: ban `require(...)` calls in TS source. Use
@@ -208,23 +208,23 @@ type noRequireImports struct{}
208
208
 
209
209
  func (noRequireImports) Name() string { return "no-require-imports" }
210
210
  func (noRequireImports) Visits() []shimast.Kind {
211
- return []shimast.Kind{shimast.KindCallExpression, shimast.KindImportEqualsDeclaration}
211
+ return []shimast.Kind{shimast.KindCallExpression, shimast.KindImportEqualsDeclaration}
212
212
  }
213
213
  func (noRequireImports) Check(ctx *Context, node *shimast.Node) {
214
- switch node.Kind {
215
- case shimast.KindCallExpression:
216
- call := node.AsCallExpression()
217
- if call == nil {
218
- return
219
- }
220
- if callCalleeName(call) != "require" {
221
- return
222
- }
223
- // Ignore `import("...")` — that's a different node kind.
224
- ctx.Report(node, "A `require()` style import is forbidden.")
225
- case shimast.KindImportEqualsDeclaration:
226
- ctx.Report(node, "An `import = require()` style import is forbidden.")
227
- }
214
+ switch node.Kind {
215
+ case shimast.KindCallExpression:
216
+ call := node.AsCallExpression()
217
+ if call == nil {
218
+ return
219
+ }
220
+ if callCalleeName(call) != "require" {
221
+ return
222
+ }
223
+ // Ignore `import("...")` — that's a different node kind.
224
+ ctx.Report(node, "A `require()` style import is forbidden.")
225
+ case shimast.KindImportEqualsDeclaration:
226
+ ctx.Report(node, "An `import = require()` style import is forbidden.")
227
+ }
228
228
  }
229
229
 
230
230
  // ban-ts-comment: `// @ts-ignore` / `// @ts-nocheck` / `// @ts-expect-error`
@@ -234,27 +234,27 @@ type banTsComment struct{}
234
234
  func (banTsComment) Name() string { return "ban-ts-comment" }
235
235
  func (banTsComment) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindSourceFile} }
236
236
  func (banTsComment) Check(ctx *Context, node *shimast.Node) {
237
- if ctx.File == nil {
238
- return
239
- }
240
- for _, directive := range ctx.File.CommentDirectives {
241
- switch directive.Kind {
242
- case shimast.CommentDirectiveKindIgnore:
243
- ctx.ReportRange(directive.Loc.Pos(), directive.Loc.End(), "Do not use `@ts-ignore` because it alters compilation errors.")
244
- case shimast.CommentDirectiveKindExpectError:
245
- ctx.ReportRange(directive.Loc.Pos(), directive.Loc.End(), "Do not use `@ts-expect-error` because it alters compilation errors.")
246
- }
247
- }
237
+ if ctx.File == nil {
238
+ return
239
+ }
240
+ for _, directive := range ctx.File.CommentDirectives {
241
+ switch directive.Kind {
242
+ case shimast.CommentDirectiveKindIgnore:
243
+ ctx.ReportRange(directive.Loc.Pos(), directive.Loc.End(), "Do not use `@ts-ignore` because it alters compilation errors.")
244
+ case shimast.CommentDirectiveKindExpectError:
245
+ ctx.ReportRange(directive.Loc.Pos(), directive.Loc.End(), "Do not use `@ts-expect-error` because it alters compilation errors.")
246
+ }
247
+ }
248
248
  }
249
249
 
250
250
  func init() {
251
- Register(noExplicitAny{})
252
- Register(noNonNullAssertion{})
253
- Register(noEmptyInterface{})
254
- Register(noInferrableTypes{})
255
- Register(noNamespace{})
256
- Register(noThisAlias{})
257
- Register(preferAsConst{})
258
- Register(noRequireImports{})
259
- Register(banTsComment{})
251
+ Register(noExplicitAny{})
252
+ Register(noNonNullAssertion{})
253
+ Register(noEmptyInterface{})
254
+ Register(noInferrableTypes{})
255
+ Register(noNamespace{})
256
+ Register(noThisAlias{})
257
+ Register(preferAsConst{})
258
+ Register(noRequireImports{})
259
+ Register(banTsComment{})
260
260
  }