TypeScript / Functional Programming

    Functional Programming w TypeScript

    Effect.ts (Effect Monad, DI, Services), fp-ts (Option, Either), neverthrow (Result type) i FP wzorce TypeScript.

    Effect.ts
    Effect platform
    fp-ts
    FP types
    neverthrow
    Result type
    Patterns
    Pure TS FP

    6 bibliotek FP dla TypeScript — porównanie

    Effect.ts, fp-ts, neverthrow, ts-results, remeda i vanilla patterns — podejście, dojrzałość i zastosowanie.

    Biblioteka Podejście Dojrzałość Kiedy
    Effect.ts Pełna platforma (Effect Monad) 1.0 (2024) Nowe projekty, full-stack TypeScript, DI
    fp-ts Haskell-inspired (Option, Either, Task) Stabilna (2.x) Istniejące projekty, algebraic types
    neverthrow Result Type (Ok/Err) Stabilna Error handling, lekka alternatywa
    ts-results Rust-like Result/Option Stabilna Rust programiści, prostszy API
    remeda Functional utilities (lodash-like) Stabilna (1.x) Data transformation, type-safe lodash
    Vanilla TS patterns Discriminated Unions, pipe Zawsze aktualne Brak zewnętrznych zależności, edukacja

    Często zadawane pytania

    Co to jest Effect.ts i dlaczego warto go znać?

    Effect.ts (Giulio Canti + Tim Smart, 2024): biblioteka do budowania efektów ubocznych w TypeScript. Typ Effect reprezentuje: sukces (A), błąd (E), wymagania (R). Effect(R, E, A) — jak ZIO w Scala. Nie Promise — Effect jest lazy (nie wykonuje się natychmiast). Dlaczego Effect: type-safe błędy (nie any). Dependency injection wbudowany. Retry, timeout, circuit breaker. Concurrency utilities. Tracing i observability. Streaming. Instalacja: npm install effect. Podstawy: import {Effect, pipe} from 'effect'. const greet = (name: string): Effect.Effect(string, never, never) => Effect.succeed('Hello, ' + name). Failure: Effect.fail(new Error('not found')). Wykonanie: Effect.runPromise(greet('Adam')).then(console.log). Effect.runSync(syncEffect). Transformacja: Effect.map(effect, fn). Effect.flatMap(effect, fn). pipe(Effect.succeed(42), Effect.map(n => n * 2), Effect.flatMap(n => ...))). Error handling: Effect.catchAll(effect, (error) => Effect.succeed(defaultValue)). Effect.retry(effect, Schedule.recurs(3)). Effect.timeout(effect, '5 seconds'). Typed błędy: type NotFound = {_tag: 'NotFound', id: string}. Effect.fail({_tag: 'NotFound', id: '1'} as NotFound). catchTag: Effect.catchTag('NotFound', err => ...). Match: Effect.match(effect, {onFailure: err => ..., onSuccess: val => ...}).

    Effect.ts — dependency injection i Services?

    Context i Services: wbudowany DI system. Services jako Context. Nie globalne singletons — przekazywane przez R (Requirements). Service definition: import {Context, Layer} from 'effect'. class UserService extends Context.Tag('UserService'){ static readonly _tag = 'UserService'. declare get: {findById: (id: string) => Effect.Effect(User, NotFound)}. }. Layer (implementacja): const UserServiceLive = Layer.effect(UserService, Effect.gen(function*() { const db = yield* DatabaseService. return {findById: (id) => db.query('SELECT...', id)} })). Kompozycja layers: const AppLayer = Layer.provide(UserServiceLive, DatabaseServiceLive). Użycie w Effect: const program = Effect.gen(function*() { const users = yield* UserService. const user = yield* users.findById('1'). return user }). Effect.provide(program, AppLayer). Testowanie: const TestUserService = Layer.succeed(UserService, {findById: (id) => Effect.succeed(mockUser)}). Effect.provide(program, TestUserService). Brak mock frameworks — wystarczy Layer swap. Generator syntax: Effect.gen(function*() { const a = yield* effectA. const b = yield* effectB. return a + b }). Jak async/await ale dla Effect. Bardziej czytelne niż pipe chains. Schema (type-safe parsing): import {Schema} from 'effect'. const User = Schema.Struct({id: Schema.String, name: Schema.String}). Schema.decodeUnknown(User)(rawData) — type-safe parse. Zod-like ale Effect-native. Lepsza integracja z Error channel.

    fp-ts — functional programming dla TypeScript?

    fp-ts (Giulio Canti): functional programming types i utilities. Poprzednik Effect.ts (był autorem obu). Option, Either, Task, TaskEither. Option(A): Some(A) | None. Brak wartości bez null/undefined. import {Option, some, none, isSome} from 'fp-ts/Option'. const result: Option(string) = some('hello'). pipe(result, Option.map(s => s.length)). fold(result, () => 0, len => len). Either(E, A): Left(E) | Right(A). Sukces lub błąd. Right = success (mnemonic: right = correct). import {Either, left, right} from 'fp-ts/Either'. const divide = (a: number, b: number): Either(string, number) => b === 0 ? left('division by zero') : right(a / b). pipe(divide(10, 2), Either.map(n => n * 2)). Task(A): lazy async computation. () => Promise(A). Brak błędów (Promise nie rzuca). TaskEither(E, A): asynchroniczne Either. Najczęściej używane. import {TaskEither, tryCatchK} from 'fp-ts/TaskEither'. const fetchUser = tryCatchK(fetchFromAPI, toError). pipe: import {pipe} from 'fp-ts/function'. pipe(value, fn1, fn2, fn3) — od lewej do prawej. Alternatywa: flow — zwraca funkcję. Reader: dependency injection. State: stateful computations. Functor, Monad, Applicative: abstrakcje. map, chain, ap. Kiedy fp-ts: istniejące projekty. Mniejsza biblioteka. Kiedy Effect: nowe projekty. Więcej utilities. Ecosystem (Effect Platform, Effect CLI). fp-ts v3: w toku — może być Effect.ts.

    neverthrow i ts-results — Result types bez ciężkiej biblioteki?

    neverthrow: lekka alternatywa do bezpiecznego error handling. Ok(T) i Err(E) — jak Rust Result. Instalacja: npm install neverthrow. import {ok, err, Result} from 'neverthrow'. function divide(a: number, b: number): Result(number, string) { if (b === 0) return err('division by zero'). return ok(a / b) }. const result = divide(10, 0). result.match({ok: val => console.log(val), err: e => console.error(e)}). Chainowanie: result.map(n => n * 2). result.andThen(n => divide(n, 3)). result.mapErr(e => new Error(e)). Async: ResultAsync. const asyncResult = ResultAsync.fromPromise(fetchUser(id), e => new FetchError(e)). await asyncResult. Combine: Result.combine([result1, result2]) — wszystkie muszą być Ok. Result.combineWithAllErrors([...]) — zbierz wszystkie błędy. ts-results: podobna biblioteka. import {Ok, Err, Result} from 'ts-results'. Option(T): Some(T) | None. Bardziej Rust-like API. Porównanie do throws: Bez Result: funkcja throws — niewidoczne w typach. Caller musi pamiętać o try/catch. Z Result: caller musi obsłużyć błąd. Type system wymaga. Porównanie neverthrow vs Effect: neverthrow — lekka (5KB). Effect — kompletna platforma. neverthrow — dla error handling. Effect — dla całego app. Kiedy nie używać Result: prostych scripts. handlery gdzie throw OK. Trzecia biblioteka (nie zawsze warto). Konsekwencja: cały team musi używać. Return type pollution. Lepiej w nowych projektach.

    Praktyczne functional programming wzorce w TypeScript bez bibliotek?

    Pure functions: brak side effects. Sama wartość decyduje o wyniku. Łatwe testowanie. Memoizable. Immutability: Object.freeze() dla readonly. Spread: {...obj, key: newVal}. Array: [...arr, newItem]. as const dla stałych. ReadonlyArray, Readonly utility types. TypeScript: Readonly(T), ReadonlyArray(T). Composition: const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x). const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x). Currying: const add = (a: number) => (b: number) => a + b. add(5)(3) === 8. Partial application. Point-free style. Discriminated Unions zamiast null: type Result = {success: true, data: User} | {success: false, error: string}. Wyczerpujące switch — TypeScript sprawdza. Brak undefined. Pattern matching (switch + satisfies): switch (result._tag) { case 'Ok': ...; case 'Err': ...; default: result satisfies never }. TypeScript check exhaustiveness. Option pattern bez biblioteki: type Option(T) = {_tag: 'Some', value: T} | {_tag: 'None'}. const some = (value) => ({_tag: 'Some', value}). const none = {_tag: 'None'}. Either bez biblioteki: type Either(E, A) = {_tag: 'Left', left: E} | {_tag: 'Right', right: A}. Monadic bind: flatMap zamiast map. Brak nested ifs. Railway oriented programming: chain operacji. Każda może zakonczyć się błędem. Happy path lub error path. Kiedy używać FP w TS: business logic. Transformation pipelines. Data processing. Nie dla: simple CRUD. Framework code. UI event handlers (side effects konieczne).

    Czytaj dalej

    Powiązane artykuły

    Kontakt

    Skontaktuj się z nami

    Porozmawiajmy o Twoim projekcie. Bezpłatna wycena w ciągu 24 godzin.

    Wyślij zapytanie

    Bezpłatna wycena w 24h
    Bez zobowiązań
    Indywidualne podejście
    Ekspresowa realizacja

    Telefon

    +48 790 814 814

    Pon-Pt: 9:00 - 18:00

    Email

    adam@fotz.pl

    Odpowiadamy w ciągu 24h

    Adres

    Plac Wolności 16

    61-739 Poznań

    Godziny pracy

    Pon - Pt9:00 - 18:00
    Sob - NdzZamknięte

    Wolisz porozmawiać?

    Zadzwoń teraz i porozmawiaj z naszym specjalistą o Twoim projekcie.

    Zadzwoń teraz

    Szanujemy Twoją prywatność

    Używamy plików cookies, aby zapewnić najlepsze doświadczenia na naszej stronie. Klikając "Akceptuję", zgadzasz się na ich użycie.