0.3.0 • Published 7 months ago

@ortense/attempt v0.3.0

Weekly downloads
-
License
MIT
Repository
github
Last release
7 months ago

Attempt banner - the attempt mascot generated by dall-e 2

@ortense/attempt

npm Coverage Status Made with Love

Uma biblioteca TypeScript minimalista para tratamento seguro de erros usando o padrão Result Type.

Instalação

npm install @ortense/attempt  # npm
yarn add  @ortense/attempt    # yarn
pnpm add @ortense/attempt     # pnpm
bun add @ortense/attempt      # bun

Por que usar?

  • 🎯 Type-safe: Tratamento de erros com segurança de tipos
  • 🎨 Elegante: API simples e expressiva
  • 🔒 Seguro: Sem exceções não tratadas
  • 🪶 Leve: Zero dependências
  • 📦 Pequeno: Bundle size mínimo
  • 💪 Robusto: 100% testado

Uso Básico

import { attempt, attemptAsync } from '@ortense/attempt'

// Código síncrono
const result = attempt(() => {
  const number = parseInt("123")
  if (isNaN(number)) throw new Error("Número inválido")
  return number
})

if (result.success) {
  console.log(result.value) // 123
} else {
  console.error(result.error) // Error
}

// Código assíncrono
const result = await attemptAsync(
  fetch("https://api.example.com/data")
    .then(response => response.json())
)

if (result.success) {
  console.log(result.value) // dados da API
} else {
  console.error(result.error.message) // "Falha na API"
}

API

Tipos

type Success<T> = {
  success: true
  value: T
}

type Failure<E extends Error> = {
  success: false
  error: E
}

type Result<T, E extends Error> = Success<T> | Failure<E>

type Match<T, E extends Error> = {
  success: (value: T) => void;
  failure: (error: E) => void;
}

Funções

success<T>(value: T): Success<T>

Cria um resultado de sucesso contendo um valor.

const result = success(42)
// { success: true, value: 42 }

match<T, E extends Error>(result: Result<T, E>, match: Match<T, E>): void

Executa diferentes callbacks baseados no estado do resultado.

match(result, {
  success: (value) => {
    console.log("Sucesso:", value)
  },
  failure: (error) => {
    console.error("Falha:", error)
  }
})

failure<E extends Error>(error: unknown): Failure<E>

Cria um resultado de falha contendo um erro.

const result = failure(new Error("Algo deu errado"))
// { success: false, error: Error("Algo deu errado") }

// Strings são convertidas para Error
const result = failure("Algo deu errado")
// { success: false, error: Error("Algo deu errado") }

attempt<T, E extends Error>(fn: () => T): Result<T, E>

Executa uma função e captura qualquer erro lançado.

const result = attempt(() => {
  if (Math.random() > 0.5) throw new Error("Azar!")
  return "Sorte!"
})

if (result.success) {
  console.log(result.value) // "Sorte!"
} else {
  console.error(result.error) // Error("Azar!")
}

attemptAsync<T, E extends Error>(Promise<T>): Promise<Result<T, E>>

Resolve uma promise e captura qualquer erro lançado.

const result = await attemptAsync(
  fetch("https://api.example.com/data")
    .then(response.json())
)

if (result.success) {
  console.log(result.value) // dados da API
} else {
  console.error(result.error) // "Falha na API"
}

Exemplos Avançados

Erros Customizados

class ApiError extends Error {
  constructor(
    message: string,
    public statusCode: number
  ) {
    super(message)
    this.name = "ApiError"
  }
}

const result = attempt(() => {
  throw new ApiError("Não autorizado", 401)
})

if (!result.success) {
  console.log(result.error.statusCode) // 401
}

Type Narrowing

function processResult(result: Result<number, Error>) {
  if (result.success) {
    // TypeScript sabe que result.value é number
    return result.value * 2
  } else {
    // TypeScript sabe que result.error é Error
    return result.error.message
  }
}

Composição de Operações

const getUser = async (id: string) => {
  const result = await attemptAsync(
    fetch(`/api/users/${id}`)
      .then(response => {
        if (!response.ok) throw new Error("Usuário não encontrado")
        return response
      })
      .then(response => response.json())
  )

  if (!result.success) {
    return result // early return com o erro
  }

  // Processa apenas em caso de sucesso
  return attempt(() => {
    const user = result.value
    if (!user.active) throw new Error("Usuário inativo")
    return user
  })
}

// Usando map para uma composição mais limpa
const getUserName = async (id: string) => {
  const result = await attemptAsync(
    fetch(`/api/users/${id}`)
      .then(response => {
        if (!response.ok) throw new Error("Usuário não encontrado")
        return response
      })
      .then(response => response.json())
  )

  return result.map(user => user.name)
}

Licença

MIT © Marcus Ortense