From harness-claude
Types async/await functions, Promise chains, concurrent patterns (Promise.all, allSettled, race), error handling, Result patterns, and retry logic in TypeScript.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Type async/await, Promise chains, and concurrent patterns correctly in TypeScript
Provides TypeScript patterns for Promises, async/await, async iterators, error handling, and type-safe wrappers when writing asynchronous code.
Provides TypeScript best practices for type-safe code including strict mode, interfaces, discriminated unions, generics, async patterns, and null safety. Useful for type definitions and maintainable TS.
Provides expertise in async patterns for Python asyncio, JS/TS promises, C# async/await, Rust futures. Covers concurrency, event loops, error handling, backpressure, cancellation, optimization.
Share bugs, ideas, or general feedback.
Type async/await, Promise chains, and concurrent patterns correctly in TypeScript
Promise<T>:async function getUser(id: string): Promise<User> {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
const [user, posts, settings] = await Promise.all([
getUser(userId), // Promise<User>
getUserPosts(userId), // Promise<Post[]>
getSettings(), // Promise<Settings>
]);
// Types: [User, Post[], Settings]
const results = await Promise.allSettled([fetchUser(), fetchPosts()]);
for (const result of results) {
if (result.status === 'fulfilled') {
console.log(result.value); // Type: User | Post[] (union of all types)
} else {
console.log(result.reason); // Type: any
}
}
const result = await Promise.race([
fetchData(), // Promise<Data>
timeout(5000), // Promise<never>
]);
// Type: Data (never is absorbed)
unknown:async function fetchUser(id: string): Promise<User> {
try {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to fetch user: ${error.message}`);
}
throw error;
}
}
type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };
async function safeFetch<T>(url: string): Promise<Result<T>> {
try {
const res = await fetch(url);
if (!res.ok) return { ok: false, error: new Error(`HTTP ${res.status}`) };
return { ok: true, value: await res.json() };
} catch (error) {
return { ok: false, error: error instanceof Error ? error : new Error(String(error)) };
}
}
async function retry<T>(
fn: () => Promise<T>,
options: { retries: number; delay: number }
): Promise<T> {
for (let i = 0; i <= options.retries; i++) {
try {
return await fn();
} catch (error) {
if (i === options.retries) throw error;
await new Promise((r) => setTimeout(r, options.delay * (i + 1)));
}
}
throw new Error('Unreachable');
}
async function mapConcurrent<T, R>(
items: T[],
fn: (item: T) => Promise<R>,
concurrency: number
): Promise<R[]> {
const results: R[] = [];
const executing = new Set<Promise<void>>();
for (const item of items) {
const p = fn(item).then((result) => {
results.push(result);
});
executing.add(p);
p.finally(() => executing.delete(p));
if (executing.size >= concurrency) {
await Promise.race(executing);
}
}
await Promise.all(executing);
return results;
}
async function* paginate<T>(
fetchPage: (cursor?: string) => Promise<{ data: T[]; nextCursor?: string }>
): AsyncGenerator<T> {
let cursor: string | undefined;
do {
const page = await fetchPage(cursor);
yield* page.data;
cursor = page.nextCursor;
} while (cursor);
}
for await (const user of paginate(fetchUsers)) {
console.log(user);
}
TypeScript types async code through the Promise<T> generic type. The await keyword unwraps Promise<T> to T, and async functions always return Promise<T>.
Awaited<T> utility type: Recursively unwraps nested Promises. Awaited<Promise<Promise<string>>> is string. Useful for typing the result of Promise.all and similar utilities.
Error typing limitations: catch blocks and .catch() callbacks receive unknown (with useUnknownInCatchVariables). TypeScript cannot track which errors a function might throw. This is by design — any function can throw any error. Use the Result pattern for typed error handling.
void vs undefined in async: An async function with no return statement returns Promise<void>, not Promise<undefined>. These are subtly different — void means "ignore the return value," undefined means "the value is undefined."
Unhandled rejection risks: Forgetting to await a Promise means errors are unhandled. TypeScript does not warn about unawaited promises by default — use the @typescript-eslint/no-floating-promises rule.
Trade-offs:
Promise.all fails fast — if one promise rejects, the entire result rejects. Use Promise.allSettled when partial success is acceptablehttps://typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html