{"id":17987793,"url":"https://github.com/dirkluijk/loadable.ts","last_synced_at":"2026-07-16T05:32:57.379Z","repository":{"id":37427189,"uuid":"291486794","full_name":"dirkluijk/loadable.ts","owner":"dirkluijk","description":"Type-safe loading states for TypeScript","archived":false,"fork":false,"pushed_at":"2024-01-05T14:36:29.000Z","size":866,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-04-29T09:15:57.721Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dirkluijk.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-08-30T14:31:06.000Z","updated_at":"2022-06-21T09:23:18.000Z","dependencies_parsed_at":"2024-10-29T19:35:06.473Z","dependency_job_id":null,"html_url":"https://github.com/dirkluijk/loadable.ts","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/dirkluijk/loadable.ts","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dirkluijk%2Floadable.ts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dirkluijk%2Floadable.ts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dirkluijk%2Floadable.ts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dirkluijk%2Floadable.ts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dirkluijk","download_url":"https://codeload.github.com/dirkluijk/loadable.ts/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dirkluijk%2Floadable.ts/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35532635,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-16T02:00:06.687Z","response_time":83,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-29T19:09:40.095Z","updated_at":"2026-07-16T05:32:57.356Z","avatar_url":"https://github.com/dirkluijk.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Loadable.ts\n\n\u003e Type-safe loading states for TypeScript\n\n[![NPM version](http://img.shields.io/npm/v/loadable.ts.svg?style=flat-square)](https://www.npmjs.com/package/loadable.ts)\n[![NPM downloads](http://img.shields.io/npm/dm/loadable.ts.svg?style=flat-square)](https://www.npmjs.com/package/loadable.ts)\n[![Build status](https://github.com/dirkluijk/loadable.ts/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/dirkluijk/loadable.ts/actions/workflows/main.yml)\n[![All Contributors](https://img.shields.io/badge/all_contributors-2-orange.svg?style=flat-square)](#contributors-)\n\n## Overview\n\nThis is a small util library to design type-safe loading states.\n\nIt includes:\n\n* Type-safe loading interfaces\n* Type-guards for loadable states\n* Useful operators for RxJS\n* Structural directives for Angular\n* Monad helpers\n\n## Getting started 🌩\n\n##### npm\n\n```\nnpm install loadable.ts\n```\n\n##### yarn\n\n```\nyarn add loadable.ts\n```\n\n## Type-safe loading interfaces\n\nIt introduces a `Loadable\u003cT, E\u003e` type that represents three possible states,\n`Loading`, `Success\u003cT\u003e` or `Failed\u003cE\u003e`.\n\n```typescript\ntype Loadable\u003cT, E = unknown\u003e = Loading | Success\u003cT\u003e | Failed\u003cE\u003e;\n```\n\nPlain TypeScript example:\n\n```typescript\nimport { LOADING, success, failed, Loadable } from 'loadable.ts';\n\nfunction getFoo(): Loadable\u003cFoo\u003e {\n    if (...) {\n        return LOADING;      // returns a `Loading`\n    } else if (...) {\n        return success(...); // returns a `Success\u003cFoo\u003e`\n    } else {\n        return failed(...);  // returns a `Failed`\n    }\n}\n\nconst foo: Loadable\u003cFoo\u003e = getFoo();\n\nif (foo.loading) {\n    // will infer to `Loading`\n    console.log('Loading...');\n} else if (foo.success) {\n    // will infer to `Success\u003cFoo\u003e` and provide value object\n    console.log(`Result: ${foo.value}`);\n} else {\n    // will infer to `Failed` and provide error object\n    console.error(`Result: ${foo.error}`);\n}\n```\n\n## Type-guards\n\nTo improve semantics and code readability, we provide the following type-guards:\n\n* `isLoading()`\n* `isSuccess()`\n* `isFailed()`\n\n```typescript\nimport { isLoading, isSuccess, Loadable } from 'loadable.ts';\n\nconst foo: Loadable\u003cFoo\u003e = getFoo();\n\nif (isLoading(foo)) {\n    // will infer to `Loading`\n    console.log('Loading...');\n} else if (isSuccess(foo)) {\n    // will infer to `Success\u003cFoo\u003e` and provide value object\n    console.log(`Result: ${foo.value}`);\n} else {\n    // will infer to `Failed` and provide error object\n    console.error(`Result: ${foo.error}`);\n}\n```\n\n## Usage with RxJS\n\nWe provide a `mapToLoadable()` operator for RxJS, which can be useful for async streams like HTTP responses.\n\n* It prepends the upstream `Observable` with a `Loading` state\n* It maps each result `T` in `Observable\u003cT\u003e` to a `Success\u003cT\u003e` state\n* It catches and maps each error `E` in the `Observable` to a `Failed\u003cE\u003e` state\n\nExample:\n\n```typescript\nfunction loadFoo(): Observable\u003cFoo\u003e {\n  // ...\n}\n\nconst foo$: Observable\u003cLoadable\u003cFoo\u003e\u003e = loadFoo().pipe(mapToLoadable());\n\n// makes use of the provided type-guards\nconst showSpinner$ = foo$.pipe(map(isLoading));\nconst showError$ = foo$.pipe(map(isFailed));\nconst fooValue$ = foo$.pipe(filter(isSuccess), map(it =\u003e it.value));\n```\n\nFurthermore, we provide the following additional RxJS operators:\n\n* `onFailed()`: shorthand for `filter(isFailed)` and `map((it) =\u003e it.error)`\n* `onSuccess()`: shorthand for `filter(isSuccess)` and `map((it) =\u003e it.value)`\n* `mapSuccess(mapFn)`: allows you to map the `value` when it is `Success`\n\n## Structural directives for Angular\n\nWe also provide three useful structural directives for Angular.\n\nThey all accept a `Loadable\u003cT\u003e` or `Observable\u003cLoadable\u003cT\u003e\u003e` input variable.\n\n* `*ifLoaded`: it will show the template when the latest value is in `Loading` state\n* `*ifFailed`: it will show the template when the latest value is in `Failed` state\n* `*ifSuccess`: it will show the template when the latest value is in `Success` state\n\n\nExample usage:\n\n```typescript\ninterface Foo {\n    name: string;\n}\n\n@Component({\n    /* ... */\n})\nclass MyComponent{\n    public foo$: Observable\u003cLoadable\u003cFoo\u003e\u003e = ...;\n    \n    /* ... */\n}\n```\n\n```html\n\u003c!-- loading state --\u003e\n\u003cdiv class=\"loading\" *ifLoading=\"foo$\"\u003e\u003c/div\u003e\n\n\u003c!-- failed state --\u003e\n\u003cdiv class=\"error\" *ifFailed=\"foo$\"\u003e\u003c/div\u003e\n\u003cdiv class=\"error\" *ifFailed=\"let error of foo$\"\u003e\n    {{ error }}\n\u003c/div\u003e\n\n\u003c!-- success state --\u003e\n\u003cdiv class=\"result\" *ifSuccess=\"foo$\"\u003e\u003c/div\u003e\n\u003cdiv class=\"result\" *ifSuccess=\"let foo of foo$\"\u003e\n    {{ foo.name }}\n\u003c/div\u003e\n```\n\n## Monads\n\nIf you want to apply operations to a `Loadable`, without the need to unwrap it, you could use the `monad()` helper function.\n\nIt returns the monadic variant `LoadableMonad` which currently provides the following operations:\n\n* `map(fn: (value: T) =\u003e R)`\n* `flatMap(fn: (value: T) =\u003e Loadable\u003cR\u003e)` \n\nExample usage:\n\n```typescript\ninterface Foo {\n    loadableBar: Loadable\u003cBar\u003e;\n}\n\ninterface Bar {\n    name: string;\n}\n\n\nconst foo: Loadable\u003cFoo\u003e = ...;\n\nconst barName: Loadable\u003cstring\u003e = monad(foo)\n    .flatMap(foo =\u003e foo.loadableBar)\n    .map(bar =\u003e bar.name);\n\n// this would be the same as:\nconst barName = isSuccess(foo) ? isSuccess(foo.value.loadableBar) ? success(foo.value.loadableBar.value.name) : foo.value.loadableBar : foo\n\n```\n\n## Contributors ✨\n\nThanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/dirkluijk\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/2102973?v=4\" width=\"100px;\" alt=\"Dirk Luijk\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eDirk Luijk\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/dirkluijk/loadable-ts/commits?author=dirkluijk\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"https://github.com/dirkluijk/loadable-ts/commits?author=dirkluijk\" title=\"Documentation\"\u003e📖\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://craftsmen.nl/\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/16564855?v=4\" width=\"100px;\" alt=\"Daan Scheerens\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eDaan Scheerens\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#ideas-dscheerens\" title=\"Ideas, Planning, \u0026 Feedback\"\u003e🤔\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c!-- markdownlint-enable --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\nThis project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdirkluijk%2Floadable.ts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdirkluijk%2Floadable.ts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdirkluijk%2Floadable.ts/lists"}