Skip to content

Modernize tooling: Migrate TSLint→ESLint, TypeScript 5.3, custom error classes#21

Draft
Copilot wants to merge 7 commits intomasterfrom
copilot/modernize-sharp-collections
Draft

Modernize tooling: Migrate TSLint→ESLint, TypeScript 5.3, custom error classes#21
Copilot wants to merge 7 commits intomasterfrom
copilot/modernize-sharp-collections

Conversation

Copy link

Copilot AI commented Feb 16, 2026

TSLint has been deprecated since 2019. This PR migrates to ESLint, upgrades TypeScript to 5.3.3, and modernizes the codebase to 2026 standards while maintaining 100% backward compatibility.

Tooling Migration

  • TSLint → ESLint 8.56: Removed deprecated TSLint dependencies, added @typescript-eslint plugins with type-aware rules
  • TypeScript 4.2 → 5.3: Updated target to ES2020, enabled stricter checks (noUnusedLocals, noUnusedParameters, noImplicitReturns, noFallthroughCasesInSwitch)
  • Build scripts: Removed hacky postbuild sed script, added type-check and prepublishOnly validation

Code Quality

  • Removed 14 @ts-ignore suppressions: Fixed with proper type assertions (as unknown as T, type guards) or documented @ts-expect-error where structurally necessary (ReadOnlyDictionary static method incompatibility)
  • Replaced tslint-disable comments: Migrated to equivalent ESLint suppressions (@typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-call)

Error Handling

Created typed error class hierarchy extending from CollectionError:

// Before: generic Error objects
throw new Error('Sequence contains no elements');

// After: typed error classes
throw new NoElementsError();

// Backward compatible via deprecated Errors class
Errors.noElements(); // Still works, returns NoElementsError instance

New error classes exported: CollectionError, ArgumentOutOfRangeError, IndexOutOfRangeError, DuplicateKeyError, NoElementsError, NoMatchError, etc.

Note: ValueNotNumberError extends TypeError (not CollectionError) to match original implementation behavior.

Configuration

Added .editorconfig for consistent formatting across editors.

Original prompt

Objective

Modernize the sharp-collections codebase to align with current TypeScript, JavaScript, and tooling best practices as of 2026.

Critical Changes

1. Replace TSLint with ESLint (CRITICAL - TSLint deprecated in 2019)

Remove these dependencies from package.json:

"tslint": "~6.1.0",
"tslint-eslint-rules": "~5.4.0",
"tslint-microsoft-contrib": "~6.2.0",
"tslint-no-unused-expression-chai": "^0.1.4",
"tslint-teamcity-reporter": "~3.2.2"

Add ESLint dependencies:

"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"eslint": "^8.56.0",
"eslint-plugin-chai-friendly": "^0.7.4"

Create .eslintrc.json:

{
  "root": true,
  "parser": "@typescript-eslint/parser",
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:@typescript-eslint/recommended-requiring-type-checking"
  ],
  "plugins": ["@typescript-eslint", "chai-friendly"],
  "parserOptions": {
    "ecmaVersion": 2022,
    "sourceType": "module",
    "project": "./tsconfig.json"
  },
  "rules": {
    "no-unused-expressions": "off",
    "chai-friendly/no-unused-expressions": "error",
    "@typescript-eslint/no-explicit-any": "warn"
  },
  "ignorePatterns": ["dist/", "node_modules/", "*.js"]
}

Create .eslintignore:

dist/
node_modules/
*.js
coverage/

Delete tslint.json file

Update package.json scripts:

"lint": "eslint src/**/*.ts tests/**/*.ts",
"lint:fix": "eslint src/**/*.ts tests/**/*.ts --fix"

2. Remove All @ts-ignore Comments

In src/extensions/enumerable/Single.ts:
Replace:

function single<T>(this: Enumerable<T>, predicate?: (x: T, index: number) => boolean): T {
    let value: T;
    let found = false;
    // ...
    // @ts-ignore
    return value;
}

With:

function single<T>(this: Enumerable<T>, predicate?: (x: T, index: number) => boolean): T {
    let value: T | undefined;
    let found = false;
    let index = 0;
    for (const element of this) {
        if (predicate == null || predicate(element, index++)) {
            if (found) {
                throw predicate != null ? Errors.moreThanOneMatch() : Errors.moreThanOneElement();
            }
            value = element;
            found = true;
        }
    }

    if (!found || value === undefined) {
        throw predicate != null ? Errors.noMatch() : Errors.noElements();
    }

    return value;
}

Find and fix any other @ts-ignore comments in the codebase

3. Update TypeScript Configuration

Update tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ES2020",
    "lib": ["ES2020", "ES2021", "ES2022"],
    "moduleResolution": "bundler",
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "outDir": "./dist",
    "downlevelIteration": true,
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "baseUrl": "./",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "exactOptionalPropertyTypes": true,
    "noUncheckedIndexedAccess": true,
    "removeComments": false
  },
  "include": ["src"],
  "exclude": ["node_modules", "**/__tests__/*", "dist"]
}

4. Modernize Error Handling with Custom Error Classes

Replace src/Errors.ts content:

/**
 * Base error class for all collection-related errors
 */
export class CollectionError extends Error {
    constructor(message: string) {
        super(message);
        this.name = 'CollectionError';
        Object.setPrototypeOf(this, CollectionError.prototype);
    }
}

/**
 * Thrown when an argument is out of range
 */
export class ArgumentOutOfRangeError extends CollectionError {
    constructor(paramName?: string) {
        super(`Argument${paramName ? ` '${paramName}'` : ''} was out of range`);
        this.name = 'ArgumentOutOfRangeError';
    }
}

/**
 * Thrown when an index is out of range
 */
export class IndexOutOfRangeError extends CollectionError {
    constructor() {
        super('Index was out of range');
        this.name = 'IndexOutOfRangeError';
    }
}

/**
 * Thrown when an index is not an integer
 */
export class IndexNotIntegerError extends CollectionError {
    constructor() {
        super('Index was not an integer');
        this.name = 'IndexNotIntegerError';
    }
}

/**
 * Thrown when an item with the same key has already been added
 */
export class DuplicateKeyError extends CollectionError {
    constructor() {
        super('An item with the same key has already been added');
        this.name = 'DuplicateKeyError';
    }
}

/**
 * Thrown when an element has already been added to a set
 */
export class DuplicateElementError extends CollectionError {
    constructor() {
        super('The element has already been added');
        this.name = 'DuplicateElementError';
    ...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

<!-- START COPILOT CODING AGENT TIPS -->
---

 Let Copilot coding agent [set things up for you](https://github.com/vdolek/sharp-collections/issues/new?title=+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 6 commits February 16, 2026 18:30
Co-authored-by: vdolek <361441+vdolek@users.noreply.github.com>
Co-authored-by: vdolek <361441+vdolek@users.noreply.github.com>
Co-authored-by: vdolek <361441+vdolek@users.noreply.github.com>
Co-authored-by: vdolek <361441+vdolek@users.noreply.github.com>
Co-authored-by: vdolek <361441+vdolek@users.noreply.github.com>
Co-authored-by: vdolek <361441+vdolek@users.noreply.github.com>
Copilot AI changed the title [WIP] Replace TSLint with ESLint for code modernization Modernize tooling: Migrate TSLint→ESLint, TypeScript 5.3, custom error classes Feb 16, 2026
Copilot AI requested a review from vdolek February 16, 2026 18:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants