tryError Documentation
TypeScript Troubleshooting
Common TypeScript issues and their solutions when using tryError.
Most TypeScript issues with tryError stem from union type inference challenges. This guide provides practical solutions to common problems.
Critical
Union Type Inference IssuesProperty 'message' does not exist on type 'never' errors
❌ Common Problem
Problem: Direct property accesstypescript
const result = await tryAsync(() => fetchData());
if (!isOk(result)) {
// ❌ TypeScript Error: Property 'message' does not exist on type 'never'
console.log(result.message);
}
✅ Solution 1: Use Type Guards
Solution: Proper type guardstypescript
const result = await tryAsync(() => fetchData());
if (isTryError(result)) {
// ✅ Type-safe access to error properties
console.log(result.message);
console.log(result.type);
console.log(result.source);
}
✅ Solution 2: Destructured Pattern
Solution: Destructure with fallbacktypescript
const result = await tryAsync(() => fetchData());
if (!isOk(result)) {
const errorMsg = isTryError(result) ? result.message : String(result);
const errorType = isTryError(result) ? result.type : 'UnknownError';
console.log(`Error (${errorType}): ${errorMsg}`);
}
Critical
Generic Type Parameter IssuesWhen TypeScript can't infer the correct error type
❌ Problem: Ambiguous return types
Problem: Generic inference failuretypescript
// TypeScript can't determine if this is T or TryError
function processData<T>(data: T): TryResult<ProcessedData, ValidationError> {
const result = trySync(() => validate(data));
// ❌ Type issues here
return result;
}
✅ Solution: Explicit Type Annotations
Solution: Explicit error typestypescript
interface ValidationError extends TryError<'ValidationError'> {
field: string;
rule: string;
}
function processData<T>(data: T): TryResult<ProcessedData, ValidationError> {
const result = trySync<ProcessedData, ValidationError>(() => {
const validation = validate(data);
if (!validation.valid) {
throw createError({
type: 'ValidationError' as const,
message: validation.message,
context: { field: validation.field, rule: validation.rule }
});
}
return process(validation.data);
});
return result;
}
Critical
Error Context Access IssuesProperty does not exist on type 'unknown' when accessing error context
❌ Common Problem
Problem: Direct context property accesstypescript
const result = await tryAsync(() => validateForm(data));
if (isTryError(result)) {
// ❌ TypeScript Error: Property 'field' does not exist on type 'unknown'
console.log(result.context.field);
console.log(result.context.rule);
}
✅ Solution 1: Type Guards
Solution: Create type guards for contexttypescript
interface ValidationContext {
field: string;
rule: string;
value: unknown;
}
function isValidationContext(context: unknown): context is ValidationContext {
return typeof context === 'object' &&
context !== null &&
'field' in context &&
'rule' in context;
}
const result = await tryAsync(() => validateForm(data));
if (isTryError(result) && isValidationContext(result.context)) {
// ✅ Type-safe access to context properties
console.log(`Field: ${result.context.field}`);
console.log(`Rule: ${result.context.rule}`);
}
✅ Solution 2: Safe Type Assertion
Solution: Type assertion with validationtypescript
const result = await tryAsync(() => validateForm(data));
if (isTryError(result)) {
// ✅ Safe assertion with optional properties
const context = result.context as { field?: string; rule?: string } | undefined;
if (context?.field && context?.rule) {
console.log(`Validation failed: ${context.field} (${context.rule})`);
} else {
console.log(`Validation failed: ${result.message}`);
}
}
✅ Solution 3: Context Helper Utility
Solution: Reusable context extractiontypescript
function getContextValue<T>(
error: TryError,
key: string,
defaultValue: T
): T {
if (typeof error.context === 'object' &&
error.context !== null &&
key in error.context) {
return (error.context as any)[key] ?? defaultValue;
}
return defaultValue;
}
const result = await tryAsync(() => validateForm(data));
if (isTryError(result)) {
const field = getContextValue(result, 'field', 'unknown');
const rule = getContextValue(result, 'rule', 'general');
console.log(`Validation failed for ${field} (rule: ${rule})`);
}
Why this happens: Error context is typed as unknown
by design to enforce type safety. Since errors can originate from anywhere with different context structures, TypeScript requires you to validate the shape before accessing properties.
Common
React Component Type IssuesTypeScript issues when using tryError in React components
❌ Problem: State type inference
Problem: useState with TryResulttypescript
function UserProfile({ userId }: { userId: string }) {
// ❌ TypeScript can't infer the correct type
const [userData, setUserData] = useState(null);
useEffect(() => {
const fetchUser = async () => {
const result = await tryAsync(() => api.getUser(userId));
setUserData(result); // ❌ Type error
};
fetchUser();
}, [userId]);
}
✅ Solution: Explicit state typing
Solution: Proper state typestypescript
interface User {
id: string;
name: string;
email: string;
}
type UserState = {
data: User | null;
error: TryError | null;
loading: boolean;
};
function UserProfile({ userId }: { userId: string }) {
const [state, setState] = useState<UserState>({
data: null,
error: null,
loading: true
});
useEffect(() => {
const fetchUser = async () => {
setState(prev => ({ ...prev, loading: true, error: null }));
const result = await tryAsync<User>(() => api.getUser(userId));
if (isOk(result)) {
setState({ data: result, error: null, loading: false });
} else {
setState({ data: null, error: result, loading: false });
}
};
fetchUser();
}, [userId]);
if (state.loading) return <LoadingSpinner />;
if (state.error) return <ErrorDisplay error={state.error} />;
if (!state.data) return <NotFound />;
return <UserData user={state.data} />;
}
TypeScript Configuration
Recommended TypeScript settings for optimal tryError experience
tsconfig.json recommendationsjson
{
"compilerOptions": {
"strict": true, // Enable all strict checks
"strictNullChecks": true, // Critical for proper type inference
"noImplicitReturns": true, // Catch missing return statements
"noImplicitAny": false, // Allow some flexibility
"exactOptionalPropertyTypes": true // Better optional property handling
}
}
Quick Reference: Type Guards
Essential type guards for working with tryError results
Essential type guardstypescript
// ✅ Check if operation succeeded
if (isOk(result)) {
// result is T (success value)
console.log(result); // Type-safe access
}
// ✅ Check if operation failed
if (isErr(result)) {
// result is TryError
console.log(result.message);
}
// ✅ Check if it's a TryError specifically
if (isTryError(result)) {
// result is TryError with all properties
console.log(result.type, result.message, result.source);
}
// ✅ Type-safe unwrapping with fallback
const value = unwrapOr(result, 'default value');
// ✅ Pattern matching style
const message = isOk(result)
? `Success: ${result}`
: `Error: ${result.message}`;
✅ Fixed: TypeScript Union Type Issues
We've added improved type guards and utilities to solve the most common TypeScript inference problems:
1. Use isTryFailure() for Error Narrowing
// ✅ RECOMMENDED: Use isTryFailure for perfect error narrowing
const result = await tryAsync(() => fetchData());
if (isTryFailure(result)) {
// TypeScript knows this is TryError - perfect inference!
console.log(result.message);
console.log(result.type);
console.log(result.source);
}
// ✅ Or use isTrySuccess for success narrowing
if (isTrySuccess(result)) {
// TypeScript knows this is your success type
console.log(result.data);
}
2. Use matchTryResult() for Perfect Type Safety
// ✅ BEST: Use matchTryResult for 100% type-safe handling
const message = matchTryResult(result, {
success: (data) => `Success: ${data.name}`, // data is properly typed
error: (error) => `Error: ${error.message}` // error is TryError
});
// No type inference issues, no manual checking needed!
3. Use unwrapTryResult() for Structured Access
// ✅ GOOD: Use unwrapTryResult for clear success/error structure
const unwrapped = unwrapTryResult(result);
if (unwrapped.success) {
console.log('Data:', unwrapped.data.name);
} else {
console.log('Error:', unwrapped.error.message);
}
// Perfect TypeScript inference with clear intent