Documentation Index
Fetch the complete documentation index at: https://docs.cbrock.dev/llms.txt
Use this file to discover all available pages before exploring further.
Your first Result
chas.fromPromise wraps any Promise and maps thrown errors into a typed value. The result is a ResultAsync<T, E> that’s awaitable, chainable, and explicit about what can go wrong.import { chas } from 'ts-chas';
interface User {
id: number;
name: string;
}
function fetchUser(id: number): chas.ResultAsync<User, string> {
return chas.fromPromise(
fetch(`/api/users/${id}`).then(res => res.json()),
(e) => `Fetch failed: ${e}`
);
}
const result = await fetchUser(1)
.map(user => user.name) // transform the success value
.mapErr(err => err.toUpperCase()); // transform the error
if (result.isOk()) {
console.log(`Hello, ${result.value}`);
} else {
console.error(result.error); // "FETCH FAILED: ..."
}
Validate input with Guard
is from ts-chas/guard gives you chainable type predicates you can use inline or combine into full schemas.import { is, defineSchemas, type InferSchema } from 'ts-chas/guard';
// Inline type predicate — works as a standard TypeScript guard
const isValidEmail = is.string.trim().email.min(5);
if (isValidEmail(someInput)) {
// someInput is narrowed to `string` here
console.log('Valid email:', someInput);
}
// Schema parsing — returns Result<T, GuardErr[]> with no try/catch
const schemas = defineSchemas({
UserPayload: {
name: is.string.min(1),
email: is.string.email,
age: is.number.gte(18).error('Must be 18 or older'),
},
});
type UserPayload = InferSchema<typeof schemas.UserPayload>;
const parsed = schemas.UserPayload.parse(incomingJson);
if (parsed.isOk()) {
saveUser(parsed.value); // typed as UserPayload
} else {
console.error(parsed.error.map(e => e.message).join('\n'));
}
A resilient Task
Task wraps an async operation lazily, which means it does not run until you call .execute(). You can attach retries, timeouts, and other resilience patterns before execution.import { Task } from 'ts-chas/task';
const fetchData = Task.from(
() => fetch('/api/data').then(res => res.json()),
(e) => new Error(`Request failed: ${e}`)
);
const result = await fetchData
.retry(3, { delay: 500, factor: 2 }) // up to 3 retries, exponential backoff
.timeout(5000, () => new Error('Request timed out'))
.execute();
if (result.isOk()) {
console.log(result.value);
}
Putting it together
Here is a ~15-line end-to-end example that validates incoming request data with Guard, fetches a remote resource with Result, and wraps the whole thing in a resilient Task.import { chas } from 'ts-chas';
import { is, defineSchemas } from 'ts-chas/guard';
import { Task } from 'ts-chas/task';
const schemas = defineSchemas({
Request: { userId: is.string.uuid('v4') },
});
function handleRequest(body: unknown) {
return Task.from(async () => {
// Validate input — parse() returns a Result, unwrap throws on error
const { userId } = schemas.Request.parse(body).unwrap();
// Fetch with explicit error typing
const user = await chas
.fromPromise(
fetch(`/api/users/${userId}`).then(r => r.json()),
(e) => new Error(`Fetch failed: ${e}`)
)
.map(data => data as { id: string; name: string });
return user;
}, (e) => e instanceof Error ? e : new Error(String(e)))
.retry(2)
.timeout(8000, () => new Error('Request timed out'));
}
const result = await handleRequest(req.body).execute();
if (result.isOk()) {
console.log('User:', result.value.name);
} else {
console.error('Error:', result.error.message);
}
These examples only scratch the surface. Head to Core Concepts for a deeper dive into Result, Guard, Task, Tagged Errors, Option, and Pipe/Flow.