TypeScript utilities that eliminate common runtime errors and make your code safer, more predictable, and easier to maintain.
Install typesafe-ts
via npm:
npm install typesafe-ts
Then use the utilities to write safer, more readable code.
+import { optional } from 'typesafe-ts';
-// working with nullables isn't ergonomic
+// Type-safe `Optional` is a more ergonomic way to work with nullables
function authenticateUser(): void {
- const user = getUser(); // returns User | null
+ optional.from(() => getUser())
- if (user) {
- authenticateWithToken(user.token);
+ .map(user => authenticateWithToken(user.token))
- } else {
+ .or_else(() => {
throw new Error('Authentication failed');
- }
+ });
}
Eliminate runtime crashes - Replace null
/undefined
with Optional
for cleaner, safer code with a more ergonomic API.
Cleaner error handling - No more nested try/catch blocks, untyped errors, or missed error cases. Embrace the "unhappy" path
Better developer experience - Full IntelliSense support and type checking catches bugs before they reach production, plus ESLint rules to ease adoption.
Minimal overhead - Optional
and Result
have 0 dependencies and won't bloat your bundles.
Utilities in this repository are expected to meet the following criteria:
A type-safe alternative to null
/undefined
for values that might be empty.
-// Unsafe: Can crash at runtime
+// Safe: Compile-time guarantees
+import { optional } from 'typesafe-ts';
-const user = getUser();
+const user = optional.from(() => getUser());
-const name = user.name.toUpperCase(); // Error: Cannot read properties of null (reading 'name')
+const name = user
+ .map(user => user.name.toUpperCase())
+ .value_or('UNKNOWN'); // provides a default value if the `user` optional is empty
console.log(name);
A type-safe alternative to throwing errors - treat errors as values with full type support.
-// Unsafe: Uncaught exceptions
-function parseConfig(input: string) {
- return JSON.parse(input); // Can throw at runtime
-}
+// Safe: Explicit error handling
+import { result } from 'typesafe-ts';
+
+function parseConfig(input: string) {
+ return result.try(() => JSON.parse(input));
+}
+
+const config = parseConfig(userInput)
+ .map(cfg => ({ ...cfg, validated: true }))
+ .match(
+ config => `Loaded: ${config.name}`,
+ error => `Failed: ${error.message}`
+ );
Both Optional
and Result
include ESLint rules to help assist with adoption and correct usage:
enforce-optional-usage
: Detects functions that return nullable types and suggests using Optional
insteadenforce-result-usage
: Detects throw statements and try/catch blocks, suggesting a Result
-based approach insteadInstall the rules alongside the main package:
Add to your ESLint 9 flat config:
import { enforceResultUsage } from "./src/result/lint.ts";
import { enforceOptionalUsage } from "./src/optional/lint.ts";
// Define the custom plugin once
const typesafe_ts = {
rules: {
"enforce-optional-usage": enforceOptionalUsage,
"enforce-result-usage": enforceResultUsage,
},
};
export default tseslint.config(
{
files: ["**/*.ts", "**/*.tsx"],
plugins: {
"typesafe-ts": typesafe_ts,
},
rules: {
"ts-utils/enforce-optional-usage": "warn",
"ts-utils/enforce-result-usage": "warn",
},
},
// other eslint config options...
);
These rules help teams adopt typesafe patterns consistently across their codebase. They are also useful for initial adoption, and for helping LLMs produce safer code.
Additions should meet all the expectations outlined above.
To validate changes:
npm run test
npm run typecheck
npm run lint
For more guidance on contributing, see the CONTRIBUTING.md file.
This repository is licensed under the MIT License. See the LICENSE.txt file for more details.
All files copied into a project should include the full license text at the top of the file.