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