-
Notifications
You must be signed in to change notification settings - Fork 1
feat: add error generic type parameter and upgrade dependencies #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
feat: add error generic type parameter and upgrade dependencies #3
Conversation
JonSilver
commented
Nov 17, 2025
- Add generic error type parameter (E = Error) to all attempt types and functions
- Allows clients to type custom error types in addition to return values
- Bump version from 1.3.9 to 1.4.0 (minor version bump)
- Upgrade all dependencies to latest versions:
- ESLint 8 → 9 (migrated to flat config)
- TypeScript 5.3 → 5.9
- Vite 5 → 7
- Vitest 0.34 → 4.0
- Prettier 2 → 3
- All other dev dependencies to latest
- Fix package.json exports order (types before import/require)
- All tests passing with new type system
- Add generic error type parameter (E = Error) to all attempt types and functions - Allows clients to type custom error types in addition to return values - Bump version from 1.3.9 to 1.4.0 (minor version bump) - Upgrade all dependencies to latest versions: - ESLint 8 → 9 (migrated to flat config) - TypeScript 5.3 → 5.9 - Vite 5 → 7 - Vitest 0.34 → 4.0 - Prettier 2 → 3 - All other dev dependencies to latest - Fix package.json exports order (types before import/require) - All tests passing with new type system
- Document new v1.4.0 feature for typing custom error types - Add TypeScript usage examples for both sync and async - Show custom error type patterns (ValidationError, ApiError) - Clarify backward compatibility (defaults to Error type)
- Add sideEffects: false for better tree-shaking - Add engines constraint (Node.js >= 18.0.0)
- Document v1.4.0 changes including error generic feature - List all dependency upgrades and build tooling changes - Follow Keep a Changelog format for consistency
- Add CI workflow for automated testing on PRs and pushes - Test on Node.js 18.x, 20.x, and 22.x - Run linting, tests, and build - Upload code coverage to Codecov - Add publish workflow for automated npm releases - Triggers on GitHub releases - Publishes to npm with provenance - Requires NPM_TOKEN secret to be configured
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds generic error type parameters to the attempt library, enabling TypeScript users to specify custom error types in addition to result types. It also includes a major upgrade of all development dependencies to their latest versions, including ESLint 9 with flat config, TypeScript 5.9, Vite 7, and Vitest 4.
Key Changes:
- Added generic error type parameter
<T, E = Error>to all attempt types and functions with backward compatibility - Upgraded tooling dependencies: ESLint 8→9 (flat config), TypeScript 5.3→5.9, Vite 5→7, Vitest 0.34→4
- Added comprehensive TypeScript usage documentation and examples in ReadMe.md
Reviewed Changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/index.ts | Added generic error type parameter E to all types and functions, enabling custom error type support |
| package.json | Bumped version to 1.4.0, reordered exports (types first), added sideEffects and engines fields, upgraded all dependencies |
| eslint.config.js | New flat config format for ESLint 9 migration |
| ReadMe.md | Added comprehensive TypeScript documentation with examples for generic error types |
| CHANGELOG.md | New changelog documenting v1.4.0 changes and upgrade details |
| .github/workflows/publish.yml | New GitHub Actions workflow for automated npm publishing |
| .github/workflows/ci.yml | New GitHub Actions workflow for continuous integration testing |
| .eslintrc.json | Removed legacy ESLint configuration file (replaced by flat config) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| checkFn(fn); | ||
|
|
||
| return Promise.resolve().then(fn).then(success).catch(fail) as Promise<AttemptReturnTuple<T>>; | ||
| return Promise.resolve().then(fn).then(success).catch(fail) as Promise<AttemptReturnTuple<T, E>>; |
Copilot
AI
Nov 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type inference for success and fail callbacks in the promise chain may not correctly infer the generic error type E. When calling .then(success).catch(fail), TypeScript cannot automatically infer the E type parameter for these generic functions, which could lead to type safety issues.
Consider explicitly passing the type parameters or creating bound versions of these functions within the attemptPromise function:
export const attemptPromise = <T, E = Error>(fn: () => Promise<T>): Promise<AttemptReturnTuple<T, E>> => {
checkFn(fn);
return Promise.resolve()
.then(fn)
.then((r: T) => success<T, E>(r))
.catch((e: any) => fail<E>(e as E)) as Promise<AttemptReturnTuple<T, E>>;
};This ensures the generic error type E is properly threaded through the promise chain.
| return Promise.resolve().then(fn).then(success).catch(fail) as Promise<AttemptReturnTuple<T, E>>; | |
| return Promise.resolve() | |
| .then(fn) | |
| .then((r: T) => success<T, E>(r)) | |
| .catch((e: any) => fail<E>(e as E)) as Promise<AttemptReturnTuple<T, E>>; |
| export const attempt = <T, E = Error>(fn: () => T): AttemptReturnTuple<T, E> => { | ||
| checkFn(fn); | ||
|
|
||
| try { | ||
| return success(fn()); | ||
| } catch (e) { | ||
| return fail(e as Error); | ||
| return fail(e as E); | ||
| } | ||
| }; |
Copilot
AI
Nov 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new generic error type parameter feature (the main feature of v1.4.0) lacks test coverage. The existing tests don't verify that custom error types work correctly with the new E type parameter.
Consider adding tests that:
- Use custom error types with both
attemptandattemptPromise - Verify that the error type is correctly inferred/enforced
- Test that the default
Errortype still works (backward compatibility)
Example test:
it("should handle custom error types", () => {
class ValidationError extends Error {
code: number;
constructor(message: string, code: number) {
super(message);
this.code = code;
}
}
const [err, result] = attempt<string, ValidationError>(() => {
throw new ValidationError("Invalid input", 400);
});
expect(err).toBeInstanceOf(ValidationError);
expect(err?.code).toBe(400);
expect(result).toBeUndefined();
});| const [error, result] = attempt<User, ValidationError>(() => { | ||
| // your code here | ||
| }); | ||
|
|
||
| // Now TypeScript knows: | ||
| // - error is ValidationError | undefined | ||
| // - result is User | undefined | ||
| ``` |
Copilot
AI
Nov 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation example references an undefined type User. The example should either define this type or use a simpler example that doesn't require additional type definitions.
Suggested fix:
// Type both result and error
class ValidationError extends Error {
code: number;
}
const [error, result] = attempt<string, ValidationError>(() => {
// your code here
return "success";
});
// Now TypeScript knows:
// - error is ValidationError | undefined
// - result is string | undefined| const [error, data] = await attemptPromise<ApiResponse, ApiError>( | ||
| async () => await fetchData() | ||
| ); | ||
|
|
||
| if (error) { | ||
| // error is typed as ApiError | ||
| console.error(error.statusCode, error.message); | ||
| } else { | ||
| // data is typed as ApiResponse | ||
| console.log(data.results); | ||
| } | ||
| ``` |
Copilot
AI
Nov 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation example references undefined types ApiResponse, ApiError, and fetchData. The example should either define these types/functions or use a simpler, self-contained example.
Suggested fix:
class ApiError extends Error {
statusCode: number;
constructor(message: string, statusCode: number) {
super(message);
this.statusCode = statusCode;
}
}
interface ApiResponse {
results: string[];
}
const [error, data] = await attemptPromise<ApiResponse, ApiError>(
async () => {
const response = await fetch('/api/data');
if (!response.ok) {
throw new ApiError('API request failed', response.status);
}
return response.json();
}
);
if (error) {
// error is typed as ApiError
console.error(error.statusCode, error.message);
} else {
// data is typed as ApiResponse
console.log(data.results);
}| ], | ||
| "sideEffects": false, | ||
| "engines": { | ||
| "node": ">=18.0.0" |
Copilot
AI
Nov 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The engines field specifies node: ">=18.0.0", but Vite 7.2.2 requires Node.js ^20.19.0 || >=22.12.0 (as seen in npm-shrinkwrap.json). This creates a conflict where the package claims to support Node.js 18.x, but the build tooling (Vite) requires at least Node.js 20.19.0.
Update the engines field to match Vite 7's requirements:
"engines": {
"node": "^20.19.0 || >=22.12.0"
}Alternatively, if Node.js 18 support is required, consider downgrading Vite to version 6.x which supports Node.js 18.
| "node": ">=18.0.0" | |
| "node": "^20.19.0 || >=22.12.0" |
|
|
||
| strategy: | ||
| matrix: | ||
| node-version: [18.x, 20.x, 22.x] |
Copilot
AI
Nov 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CI workflow tests against Node.js 18.x, but Vite 7.2.2 (used in the build) requires Node.js ^20.19.0 || >=22.12.0. This means the CI build step will fail on Node.js 18.x.
Update the matrix to only include Node.js versions compatible with Vite 7:
matrix:
node-version: [20.x, 22.x]Or add a separate job that tests on Node 18 but skips the build step, if runtime compatibility with Node 18 is desired.
| node-version: [18.x, 20.x, 22.x] | |
| node-version: [20.x, 22.x] |
| - Example: `attempt<User, ValidationError>(() => ...)` | ||
| - Package metadata: | ||
| - `sideEffects: false` for better tree-shaking support | ||
| - `engines` field specifying Node.js >= 18.0.0 |
Copilot
AI
Nov 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The changelog incorrectly states that the engines field specifies "Node.js >= 18.0.0". However, Vite 7.2.2 (one of the upgraded dependencies) requires Node.js ^20.19.0 || >=22.12.0, which means Node.js 18.x is not actually supported.
Update the changelog to reflect the actual Node.js version requirement, or adjust the dependencies to support Node.js 18 if that's the intended target.
| - `engines` field specifying Node.js >= 18.0.0 | |
| - `engines` field specifying Node.js ^20.19.0 || >=22.12.0 |