Skip to content

Commit ff34546

Browse files
cmdcolinclaude
andcommitted
re-enable typescript-eslint rules and fix violations
Re-enable no-explicit-any (error), no-this-alias, no-non-null-assertion, and the no-unsafe-* rule cluster by fixing their root causes: - Add types:["node"] to tsconfig.lint.json so @types/node resolves - Fix test imports to use explicit .ts extensions and node: prefix (required by moduleResolution: nodenext) - Replace ~30 non-null assertions with ?? '' / conditional guards - Cast JSON.parse result to Record<string,unknown> at use site Also simplify parseMetaString (for-of over parts array, .map for trim), parseBreakend (for-of over tokens array), and parse.ts (direct match index access instead of match.slice(1,3)). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 2912183 commit ff34546

11 files changed

+46
-53
lines changed

eslint.config.mjs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export default defineConfig(
3434
{
3535
rules: {
3636
curly: 'error',
37+
eqeqeq: 'error',
3738
'@typescript-eslint/consistent-type-imports': 'error',
3839
'no-console': [
3940
'warn',
@@ -61,14 +62,7 @@ export default defineConfig(
6162
'error',
6263
{ 'ts-expect-error': 'allow-with-description', 'ts-ignore': true },
6364
],
64-
'@typescript-eslint/no-this-alias': 'off',
65-
'@typescript-eslint/no-unsafe-member-access': 'off',
66-
'@typescript-eslint/no-unsafe-argument': 'off',
67-
'@typescript-eslint/no-explicit-any': 'warn',
68-
'@typescript-eslint/no-unsafe-assignment': 'off',
69-
'@typescript-eslint/no-unsafe-call': 'off',
70-
'@typescript-eslint/no-unsafe-return': 'off',
71-
'@typescript-eslint/no-non-null-assertion': 'off',
65+
'@typescript-eslint/no-explicit-any': 'error',
7266
'@typescript-eslint/restrict-template-expressions': 'off',
7367
'@typescript-eslint/prefer-for-of': 'off',
7468

src/parse.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,8 @@ export default class VCFParser {
8787
if (!match) {
8888
throw new Error(`Line is not a valid metadata line: ${line}`)
8989
}
90-
const [metaKey, metaVal] = match.slice(1, 3)
91-
92-
const r = metaKey!
90+
const r = match[1] ?? ''
91+
const metaVal = match[2]
9392
if (metaVal?.startsWith('<')) {
9493
if (!(r in this.metadata)) {
9594
this.metadata[r] = {}
@@ -147,7 +146,9 @@ export default class VCFParser {
147146
if (typeof filteredMetadata !== 'object' || filteredMetadata === null) {
148147
return undefined
149148
}
150-
filteredMetadata = (filteredMetadata as Record<string, unknown>)[args[i]!]
149+
filteredMetadata = (filteredMetadata as Record<string, unknown>)[
150+
args[i] ?? ''
151+
]
151152
if (!filteredMetadata) {
152153
return filteredMetadata
153154
}

src/parseBreakend.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ export function parseBreakend(breakendString: string): Breakend | undefined {
2424
let Join
2525
let Replacement
2626
let MatePosition
27-
const tokensLen = tokens.length
28-
for (let i = 0; i < tokensLen; i++) {
29-
const tok = tokens[i]!
27+
for (const tok of tokens) {
3028
if (tok) {
3129
if (tok.includes(':')) {
3230
MatePosition = tok
@@ -69,7 +67,7 @@ export function parseBreakend(breakendString: string): Breakend | undefined {
6967
Join: 'left',
7068
Replacement,
7169
MateDirection: 'right',
72-
MatePosition: `<${res[1]!}>:1`,
70+
MatePosition: `<${res[1] ?? ''}>:1`,
7371
}
7472
: undefined
7573
}
@@ -85,7 +83,7 @@ export function parseBreakend(breakendString: string): Breakend | undefined {
8583
Join: 'right',
8684
Replacement,
8785
MateDirection: 'right',
88-
MatePosition: `<${res[2]!}>:1`,
86+
MatePosition: `<${res[2] ?? ''}>:1`,
8987
}
9088
: undefined
9189
}

src/parseMetaString.ts

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
1-
// constructed with the assistance of claude AI
2-
//
3-
// I first prompted it with a regex that splits a comma separated string with
4-
// awareness of quotation from this stackoverflow question
5-
// https://stackoverflow.com/a/18893443/2129219, and asked it to add support
6-
// for square brackets
7-
//
8-
// it undid the regex into serial logic and the result was this function
91
function customSplit(str: string) {
102
const result = []
113
const chars = []
@@ -48,15 +40,16 @@ export function parseMetaString(metaString: string) {
4840
const inside = metaString.slice(1, -1)
4941
const parts = customSplit(inside)
5042
const entries: [string, string | string[]][] = []
51-
for (let i = 0; i < parts.length; i++) {
52-
const f = parts[i]!
43+
for (const f of parts) {
5344
const [key, val] = splitFirst(f, '=')
5445
if (val && val.startsWith('[') && val.endsWith(']')) {
55-
const items = val.slice(1, -1).split(',')
56-
for (let j = 0; j < items.length; j++) {
57-
items[j] = items[j]!.trim()
58-
}
59-
entries.push([key, items])
46+
entries.push([
47+
key,
48+
val
49+
.slice(1, -1)
50+
.split(',')
51+
.map(s => s.trim()),
52+
])
6053
} else if (val && val.startsWith('"') && val.endsWith('"')) {
6154
entries.push([key, val.slice(1, -1)])
6255
} else {

test/index.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { describe, expect, it } from 'vitest'
22

3-
import { parseBreakend } from '../src'
3+
import { parseBreakend } from '../src/index.ts'
44

5-
import type { Breakend } from '../src'
5+
import type { Breakend } from '../src/index.ts'
66

77
describe('testBreakend', () => {
88
it('can parse breakends', () => {

test/parse.test.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import fs from 'fs'
1+
import { readFileSync } from 'node:fs'
22

33
import { expect, test } from 'vitest'
44

5-
import VCF, { Variant, parseBreakend } from '../src'
5+
import VCF, { Variant, parseBreakend } from '../src/index.ts'
66

77
const readVcf = (file: string) => {
8-
const f = fs.readFileSync(file, 'utf8')
8+
const f = readFileSync(file, 'utf8')
99
const lines = f.split('\n')
1010
const header = [] as string[]
1111
const rest = [] as string[]
@@ -123,7 +123,7 @@ test('sniffles vcf', () => {
123123
const VCFParser = new VCF({
124124
header,
125125
})
126-
const variant = VCFParser.parseLine(lines[0])
126+
const variant = VCFParser.parseLine(lines[0] ?? '')
127127
expect(variant).toMatchSnapshot()
128128
expect(variant.SAMPLES()).toMatchSnapshot()
129129
})
@@ -133,8 +133,8 @@ test('can parse a line from the VCF spec Y chrom (haploid))', () => {
133133
const VCFParser = new VCF({
134134
header,
135135
})
136-
const variant = VCFParser.parseLine(lines[0])
137-
const variant2 = VCFParser.parseLine(lines[1])
136+
const variant = VCFParser.parseLine(lines[0] ?? '')
137+
const variant2 = VCFParser.parseLine(lines[1] ?? '')
138138
expect(variant).toMatchSnapshot()
139139
expect(variant.SAMPLES()).toMatchSnapshot()
140140
expect(variant2).toMatchSnapshot()
@@ -176,7 +176,7 @@ test('test no info strict', () => {
176176
header,
177177
strict: true,
178178
})
179-
expect(() => VCFParser.parseLine(lines[0])).toThrow(/INFO/)
179+
expect(() => VCFParser.parseLine(lines[0] ?? '')).toThrow(/INFO/)
180180
})
181181

182182
test('test no info non-strict', () => {
@@ -185,8 +185,8 @@ test('test no info non-strict', () => {
185185
header,
186186
strict: false,
187187
})
188-
expect(VCFParser.parseLine(lines[0])).toBeTruthy()
189-
expect(VCFParser.parseLine(lines[0]).GENOTYPES()).toEqual({})
188+
expect(VCFParser.parseLine(lines[0] ?? '')).toBeTruthy()
189+
expect(VCFParser.parseLine(lines[0] ?? '').GENOTYPES()).toEqual({})
190190
})
191191

192192
test('empty header lines', () => {
@@ -205,7 +205,7 @@ test('shortcut parsing with vcf 4.3 bnd example', () => {
205205

206206
const VCFParser = new VCF({ header })
207207
const variants = lines.map(line => VCFParser.parseLine(line))
208-
expect(variants.map(m => m.ALT?.[0].toString())).toEqual(
208+
expect(variants.map(m => m.ALT?.[0]?.toString())).toEqual(
209209
lines.map(line => line.split('\t')[4]),
210210
)
211211

@@ -250,8 +250,9 @@ test('sample to genotype information', () => {
250250
const VCFParser = new VCF({
251251
header,
252252
})
253-
expect(VCFParser.getMetadata().META).toMatchSnapshot()
254-
expect(VCFParser.getMetadata().SAMPLES).toMatchSnapshot()
253+
const metadata = VCFParser.getMetadata() as Record<string, unknown>
254+
expect(metadata.META).toMatchSnapshot()
255+
expect(metadata.SAMPLES).toMatchSnapshot()
255256
})
256257

257258
test('pedigree', () => {
@@ -311,7 +312,10 @@ test('Variant serializes without methods', () => {
311312
const variant = VCFParser.parseLine(
312313
'20\t14370\trs6054257\tG\tA\t29\tPASS\tNS=3;DP=14;AF=0.5;DB;H2\tGT:GQ:DP:HQ\t0|0:48:1:51,51\t1|0:48:8:51,51\t1/1:43:5:.,.',
313314
)
314-
const serialized = JSON.parse(JSON.stringify(variant))
315+
const serialized = JSON.parse(JSON.stringify(variant)) as Record<
316+
string,
317+
unknown
318+
>
315319

316320
expect(serialized.SAMPLES).toBeUndefined()
317321
expect(serialized.GENOTYPES).toBeUndefined()

test/parseGenotypesOnly-edge-cases.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { expect, test } from 'vitest'
22

3-
import { parseGenotypesOnly } from '../src/parseGenotypesOnly'
3+
import { parseGenotypesOnly } from '../src/parseGenotypesOnly.ts'
44

55
test('last sample with 3-char GT and no trailing tab', () => {
66
// Single sample, no trailing tab
@@ -120,7 +120,7 @@ test('haploid genotypes - many samples', () => {
120120

121121
const expected = {} as Record<string, string>
122122
samples.forEach((s, i) => {
123-
expected[s] = gts[i]!
123+
expected[s] = gts[i] ?? ''
124124
})
125125

126126
expect(result).toEqual(expected)
@@ -368,7 +368,7 @@ test('large scale mixed ploidy', () => {
368368

369369
const expected = {} as Record<string, string>
370370
samples.forEach((s, i) => {
371-
expected[s] = gts[i]!
371+
expected[s] = gts[i] ?? ''
372372
})
373373

374374
expect(result).toEqual(expected)

test/parseGenotypesOnly-ultrafast-edge.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { expect, test } from 'vitest'
22

3-
import { parseGenotypesOnly } from '../src/parseGenotypesOnly'
3+
import { parseGenotypesOnly } from '../src/parseGenotypesOnly.ts'
44

55
test('ultra-fast path should not be tricked by mixed ploidy with matching length', () => {
66
// 4 samples with total length = 15 = 4*4-1

test/parseGenotypesOnly.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { expect, test } from 'vitest'
22

3-
import { parseGenotypesOnly } from '../src/parseGenotypesOnly'
3+
import { parseGenotypesOnly } from '../src/parseGenotypesOnly.ts'
44

55
test('parse genotypes', () => {
66
expect(parseGenotypesOnly('GT', './.\t./.', ['h1', 'h2'])).toMatchSnapshot()

test/parseMetaString.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { expect, test } from 'vitest'
22

3-
import { parseMetaString } from '../src/parseMetaString'
3+
import { parseMetaString } from '../src/parseMetaString.ts'
44

55
test('array in values', () => {
66
const result1 = parseMetaString(

0 commit comments

Comments
 (0)