Skip to content

Commit

Permalink
Support ts-expect-error for specific version ranges (#1021)
Browse files Browse the repository at this point in the history
  • Loading branch information
jakebailey committed Jun 26, 2024
1 parent d2269e7 commit 8100279
Show file tree
Hide file tree
Showing 11 changed files with 243 additions and 33 deletions.
5 changes: 5 additions & 0 deletions .changeset/sixty-items-sin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@definitelytyped/eslint-plugin": patch
---

Support ts-expect-error for specific version ranges
8 changes: 6 additions & 2 deletions packages/eslint-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"dependencies": {
"@definitelytyped/utils": "workspace:*",
"@typescript-eslint/types": "^7.14.1",
"@typescript-eslint/utils": "^7.14.1"
"@typescript-eslint/utils": "^7.14.1",
"semver": "^7.5.4"
},
"peerDependencies": {
"@typescript-eslint/eslint-plugin": "^7.14.1",
Expand All @@ -36,10 +37,13 @@
"devDependencies": {
"@definitelytyped/eslint-plugin": "link:",
"@types/eslint": "^8.56.2",
"@types/semver": "^7.5.6",
"glob": "^10.3.10",
"jest-file-snapshot": "^0.5.0",
"strip-ansi": "^6.0.1",
"typescript": "^5.5.2"
"typescript": "^5.5.2",
"typescript-5.4": "npm:typescript@~5.4.0-0",
"typescript-5.5": "npm:typescript@~5.5.0-0"
},
"engines": {
"node": ">=18.18.0"
Expand Down
25 changes: 25 additions & 0 deletions packages/eslint-plugin/src/rules/expect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type * as ts from "typescript";
import path from "path";
import fs from "fs";
import { ReportDescriptorMessageData } from "@typescript-eslint/utils/ts-eslint";
import * as semver from "semver";

type TSModule = typeof ts;

Expand Down Expand Up @@ -241,6 +242,10 @@ const zeroSourceLocation: Readonly<TSESTree.SourceLocation> = {

const expectTypeToken = "$ExpectType";

// Based on TypeScript's scanner.ts
const expectErrorSingleLine = /^\/\/\/?\s*@ts-expect-error\s+(.*)/
const expectErrorMultiLine = /^(?:\/|\*)*\s*@ts-expect-error\s+(.*)/;

function walk(
getLocFromIndex: (index: number) => Readonly<TSESTree.Position>,
report: Reporter,
Expand All @@ -259,6 +264,26 @@ function walk(
// Don't care about emit errors.
const diagnostics = ts.getPreEmitDiagnostics(program, sourceFile);
for (const diagnostic of diagnostics) {
if (diagnostic.code === 2578) {
// This is a ts-expect-error; parse "// @ts-expect-error <range>" and ignore when the current version is outside that range.
const text = sourceFile.text.slice(diagnostic.start!, diagnostic.start! + diagnostic.length!);
const match = text.match(expectErrorSingleLine) || text.match(expectErrorMultiLine);
if (match) {
let range: semver.Range | undefined;
try {
range = new semver.Range(match[1].trim())
} catch {
// Ignore any parsing errors.
}

if (range) {
if (!semver.satisfies(versionName, range, { loose: true })) {
continue;
}
}
}
}

addDiagnosticFailure(diagnostic);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
types/expect-error-range/expect-error-range-tests.ts
8:11 error [email protected] compile error:
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string' @definitelytyped/expect
11:11 error [email protected] compile error:
Type 'string' is not assignable to type 'never' @definitelytyped/expect
16:5 error [email protected] compile error:
Unused '@ts-expect-error' directive @definitelytyped/expect
20:5 error [email protected] compile error:
Unused '@ts-expect-error' directive @definitelytyped/expect
27:5 error [email protected] compile error:
Unused '@ts-expect-error' directive @definitelytyped/expect
31:5 error [email protected] compile error:
Unused '@ts-expect-error' directive @definitelytyped/expect

✖ 6 problems (6 errors, 0 warnings)

==== types/expect-error-range/expect-error-range-tests.ts ====

// In TypeScript <5.5, elem will have type "string",
// but 5.5 it will be "string | undefined".
const elem = ["value", undefined].filter(x => x != null)[0];


{
// This should error in 5.4, but not 5.5.
const test1: string = elem;
~~~~~
!!! @definitelytyped/expect: [email protected] compile error:
!!! : Type 'string | undefined' is not assignable to type 'string'.
!!! : Type 'undefined' is not assignable to type 'string'.

// This should error in 5.5, but not 5.4.
const test2: undefined extends typeof elem ? typeof elem : never = elem;
~~~~~
!!! @definitelytyped/expect: [email protected] compile error:
!!! : Type 'string' is not assignable to type 'never'.
}

{
// This should error in 5.5, but not 5.4.
// @ts-expect-error
~~~~~~~~~~~~~~~~~~~
!!! @definitelytyped/expect: [email protected] compile error:
!!! : Unused '@ts-expect-error' directive.
const test1: string = elem;

// This should error in 5.4, but not 5.5.
// @ts-expect-error
~~~~~~~~~~~~~~~~~~~
!!! @definitelytyped/expect: [email protected] compile error:
!!! : Unused '@ts-expect-error' directive.
const test2: undefined extends typeof elem ? typeof elem : never = elem;
}

// These should be treated as though there is no range.
{
// This should error in 5.5, but not 5.4.
// @ts-expect-error random non-range text
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! @definitelytyped/expect: [email protected] compile error:
!!! : Unused '@ts-expect-error' directive.
const test1: string = elem;

// This should error in 5.4, but not 5.5.
// @ts-expect-error random non-range text
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! @definitelytyped/expect: [email protected] compile error:
!!! : Unused '@ts-expect-error' directive.
const test2: undefined extends typeof elem ? typeof elem : never = elem;
}

// None of these expects should error.
{
// @ts-expect-error <5.5
const test1: string = elem;

// @ts-expect-error >=5.5
const test2: undefined extends typeof elem ? typeof elem : never = elem;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
No errors

==== types/expect-error-range/index.d.ts ====

export const value: number;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module.exports = {
settings: {
dt: {
versionsToTest: [
{ versionName: "5.4", path: "typescript-5.4" },
{ versionName: "5.5", path: "typescript-5.5" }
]
}
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// In TypeScript <5.5, elem will have type "string",
// but 5.5 it will be "string | undefined".
const elem = ["value", undefined].filter(x => x != null)[0];


{
// This should error in 5.4, but not 5.5.
const test1: string = elem;

// This should error in 5.5, but not 5.4.
const test2: undefined extends typeof elem ? typeof elem : never = elem;
}

{
// This should error in 5.5, but not 5.4.
// @ts-expect-error
const test1: string = elem;

// This should error in 5.4, but not 5.5.
// @ts-expect-error
const test2: undefined extends typeof elem ? typeof elem : never = elem;
}

// These should be treated as though there is no range.
{
// This should error in 5.5, but not 5.4.
// @ts-expect-error random non-range text
const test1: string = elem;

// This should error in 5.4, but not 5.5.
// @ts-expect-error random non-range text
const test2: undefined extends typeof elem ? typeof elem : never = elem;
}

// None of these expects should error.
{
// @ts-expect-error <5.5
const test1: string = elem;

// @ts-expect-error >=5.5
const test2: undefined extends typeof elem ? typeof elem : never = elem;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const value: number;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "@types/expect-error-range",
"version": "2.0.9999",
"owners": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6",
"dom"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"expect-error-range-tests.ts"
]
}
Loading

0 comments on commit 8100279

Please sign in to comment.