{"id":22870692,"url":"https://github.com/muthuishere/declarative-optional","last_synced_at":"2025-10-12T15:33:13.190Z","repository":{"id":64141371,"uuid":"443700112","full_name":"muthuishere/declarative-optional","owner":"muthuishere","description":"A Declarative way to deal with null , undefined and promises via optional and streams","archived":false,"fork":false,"pushed_at":"2023-12-27T05:19:05.000Z","size":682,"stargazers_count":2,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-26T10:54:34.080Z","etag":null,"topics":["async","javascript","optional","promise"],"latest_commit_sha":null,"homepage":"","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/muthuishere.png","metadata":{"files":{"readme":"README.md","changelog":null,"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}},"created_at":"2022-01-02T06:42:27.000Z","updated_at":"2024-01-12T18:25:44.000Z","dependencies_parsed_at":"2023-12-27T06:28:12.839Z","dependency_job_id":"829e756b-f8e9-4f40-b63a-bb019c335702","html_url":"https://github.com/muthuishere/declarative-optional","commit_stats":{"total_commits":59,"total_committers":1,"mean_commits":59.0,"dds":0.0,"last_synced_commit":"4b7c819604815f8cb871220803f5302294182f8b"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/muthuishere/declarative-optional","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/muthuishere%2Fdeclarative-optional","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/muthuishere%2Fdeclarative-optional/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/muthuishere%2Fdeclarative-optional/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/muthuishere%2Fdeclarative-optional/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/muthuishere","download_url":"https://codeload.github.com/muthuishere/declarative-optional/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/muthuishere%2Fdeclarative-optional/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279011844,"owners_count":26085004,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-12T02:00:06.719Z","response_time":53,"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":["async","javascript","optional","promise"],"created_at":"2024-12-13T13:15:43.860Z","updated_at":"2025-10-12T15:33:13.142Z","avatar_url":"https://github.com/muthuishere.png","language":"TypeScript","readme":"\n![Coverage](https://img.shields.io/codecov/c/github/muthuishere/declarative-optional)\n![License](https://img.shields.io/npm/l/declarative-optional)\n![Version](https://img.shields.io/npm/v/declarative-optional)\n\n\nDeclarative-Optional\n===================\n\nA Javascript library to write concise functional code.Combined with features of Java Optional \u0026 Javascripts Promise chaining\n\n##### Features\n\n\u003e Lazily evaluated\n\n\u003e chaining async and sync functions\n\n\u003e Most of the Java Optional Features\n\n\u003e Some of the Java Stream Features\n\n\n\n\u003chr/\u003e\n\n##### Installation\n\n```\n    npm install declarative-optional   \n```\n\n\n##### Usage\nTo Import\n```\n// Common JS\n    const {Optional} = require( \"declarative-optional\");\n\n//ES6\n    import {Optional} from \"declarative-optional\";\n\n```\n\nCommon Usages\n\n```\n\n//Increment By 5 \n  function incrementByFive(input) {\n        return Optional.of(input)\n            .map(i=\u003ei+5)\n            .orElse(0)\n    }\n\n\n// incrementByFive(41)  =\u003e 46\n// incrementByFive(null)  =\u003e 0\n// incrementByFive(undefined)  =\u003e 0\n\n\n\n// All the expressions will be evaluated only after you specified get()\n\n//Increment a Number by 5, Only if its dividable by 5\nOptional.of(input)\n    .filter(val=\u003eval % 5 == 0)\n    .map(val=\u003eval+5)\n    .get()\n\n\n   \n```\n\n\nOn  Asynchronous code\n\n```javascript\n// Consider the async function\n\nfunction getFromUserService({username, password}) {\n    return new Promise((function (resolve) {\n        resolve({name: \"user\", isAdmin: true})\n    }))\n}\n\nasync function login({username, password}) {\n\n    if (null == username || null == password) {\n        throw new Error(\"Cannot be Null\")\n    }\n\n    const result = await getFromUserService(username, password)\n\n\n    if (result.isAdmin) {\n        redirectTo(\"adminPage\")\n    } else {\n        redirectTo(\"userPage\")\n    }\n\n}\n\n// With Declarative Optional\nasync function login({username, password}) {\n\n\n    const page = await Optional.of({username: user, password: pass})\n        .filter(({username, password}) =\u003e (null != username \u0026\u0026 null != password))\n        .map(getFromUserService)\n        .map(result =\u003e result.isAdmin ? \"adminPage\" : \"userPage\")\n        .toAsync();\n\n\n    page.ifPresentOrElse(redirectTo, () =\u003e {\n        throw new Error(\"Cannot be Null\")\n    })\n}\n```\n\n\n#### fetch Api with Optional \n\n```javascript\n\n    // Typical code \n\n    const url  ='https://jsonplaceholder.typicode.com/todos/' + item\n    const rawResults = await fetch(url);\n    const response = await rawResults.json();\n\n    if (response.completed) {\n        return response.title\n    } else {\n        return null\n    }\n\n    \n    // Can be rewritten with optional as \n    return await Optional.of('https://jsonplaceholder.typicode.com/todos/' + item)\n        .map(fetch)\n        .map(response =\u003e response.json())\n        .filter(response =\u003e response.completed == true)\n        .map(response =\u003e response.title)\n        .getAsync();\n\n   \n    \n\n\n```\nThere are so much you can play with declarative suite. It does have some features similar to Java Optional \u0026 RX libraries, except the code is  small (one file around 4 Kb original source) and simple. \n\n\nIt also features a stream library,  a wrapper for array , with lazy evaluation and functional programming features.\n\n\n```javascript\n\n//commonjs\n    const {Stream} = require(\"declarative-optional\");\n\n//ES6\n    import {Stream} from \"declarative-optional\";\n    \n    \n```\n\n\n\n```javascript\n\n    const results = Stream.of([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])\n        .filter(val =\u003e val % 2 == 0)\n        .map(val =\u003e val * 2)\n        .get();\n\n    console.log(stream) // [4,8,12,16,20]\n    \n    \n    // first\n    Stream.of([45,46,80])\n        .first()\n        .get()\n    // [45]\n    \n    \n    \n    // last\n    Stream.of([45,46,80])\n        .last()\n        .get()\n    // [80]\n    \n    //The first and last methods are useful when you want to get the first or last element of a stream and also you can chain up map operations.\n\n    Stream.of([45,46,80])\n        .last()\n        .map(val=\u003eval*2)\n        .get()\n    // [160]\n    \n\n// Async Stream\n    const input = [Promise.resolve(21),Promise.resolve(25),Promise.resolve(30)]\n    const result = await Stream.of(input)\n        .filter((value) =\u003e value %5 ==0)\n        .map(getFromUserService)\n        .getAsync()\n    \n    // [25,30]\n    \n    \n// handle async errors or empty values with default value\n\n    const input = [Promise.resolve(21),Promise.reject(25),Promise.resolve(30)]\n    const val = await Stream.of(input)\n        .filter((value) =\u003e value %5 ==0)\n        .map(getFromUserService)\n        .orElseAsync([\"default\"])\n\n    // [\"default\"]    \n\n\n```\n\n\n### Documentation\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e Function \u003c/td\u003e \u003ctd\u003e Description \u003c/td\u003e\u003ctd\u003e Example \u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\n    \u003ctd\u003e Optional.of \u003c/td\u003e\n    \u003ctd\u003e To create a new Optional .Can be value or promise or null or undefined \u003c/td\u003e\n    \u003ctd\u003e  \n\u003cbr/\u003e\n\n     Optional.of(input)\n\n\u003cbr/\u003e\n    \u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n    \u003ctd\u003e map \u003c/td\u003e\n    \u003ctd\u003e\n        convert from one form to other, returns optional instance, can return async functions as well\n    \u003c/td\u003e\n    \u003ctd\u003e\n\u003cbr/\u003e\n\n      Optional.of(21)\n      .map(val =\u003e val + 1)\n\n\n\u003cbr/\u003e\n    \u003c/td\u003e\n\n\u003c/tr\u003e\n\u003ctr\u003e\n    \u003ctd\u003e filter \u003c/td\u003e\n    \u003ctd\u003e\n        apply a predicate function over a value\n    \u003c/td\u003e\n    \u003ctd\u003e\n\u003cbr/\u003e\n\n      Optional.of(21)\n      .filter(val =\u003e val % 5 == 0)\n\n\u003cbr/\u003e\n    \u003c/td\u003e\n\n\u003c/tr\u003e\n\u003ctr\u003e\n    \u003ctd\u003e flatmap \u003c/td\u003e\n    \u003ctd\u003e\nif an existing function returns Optional , it will flatten the value and pass it below         \n    \u003c/td\u003e\n    \u003ctd\u003e\n\n\u003cbr/\u003e\n\n    // Consider a function which will return Optional\n    //returns Optional \n    const powersOf = (name)=\u003eOptional.of(['web-shooters','syntheitic-webbing'])\n    \n    const superHeroById = (id) =\u003e  \"Spider-Man\"\n\n\u003cbr/\u003e\n\n    const res  = Optional.of(52001)\n        .map(superHeroById)\n        .map(powersOf )\n        .flatten()\n        .get()\n\n    // results ['web-shooters','syntheitic-webbing']\n\n\n\u003cbr/\u003e\n\n    const res  = Optional.of(52001)\n        .map(superHeroById)\n        .flatmap(powersOf )\n        .get()\n\n    // results ['web-shooters','syntheitic-webbing']\n\n\n\n\n\n\u003cbr/\u003e\n    \u003c/td\u003e\n\n\u003c/tr\u003e\n\n\u003ctr\u003e\n    \u003ctd\u003e get \u003c/td\u003e\n    \u003ctd\u003e\nevaluate all the chained functions and give the result. If no value is available , will return null        \n    \u003c/td\u003e\n    \u003ctd\u003e\n\u003cbr/\u003e\n\n      Optional.of(21)\n      .filter(val =\u003e val % 5 == 0)\n      .map(val =\u003e val + 5)\n      .get() ;  // returns null\n\n\u003cbr/\u003e\n\n      Optional.of(20)\n      .filter(val =\u003e val % 5 == 0)\n       .map(val =\u003e val + 5)\n      .get() ;  // returns 25\n\n\u003cbr/\u003e\n\n***Error***\n\n      Optional.of(input)      \n       .map(promiseFunctionToValidateUserDetails)\n       .map(promiseFunctionToValidateSomeOther)\n      .get() ;  \n\n    // Error ? Use getAsync to deal with promises\n\n\u003cbr/\u003e\n    \u003c/td\u003e\n\n\u003c/tr\u003e\n\u003ctr\u003e\n    \u003ctd\u003e orElse \u003c/td\u003e\n    \u003ctd\u003e\nEvaluate all the chained functions and if result exists, give the result. If no value is available , will return value passed by        \n    \u003c/td\u003e\n    \u003ctd\u003e\n\u003cbr/\u003e\n\n      Optional.of(21)\n      .filter(val =\u003e val % 5 == 0)\n      .map(val =\u003e val + 5)\n      .orElse(45) ;  // returns 45 , as the evaluation will return null\n\n\u003cbr/\u003e\n\n      Optional.of(20)\n      .filter(val =\u003e val % 5 == 0)\n       .map(val =\u003e val + 5)\n      .orElse(45) ;  // returns 25\n\n\n\u003cbr/\u003e\n    \u003c/td\u003e\n\n\u003c/tr\u003e\n\n\u003ctr\u003e\n    \u003ctd\u003e stream \u003c/td\u003e\n    \u003ctd\u003e\nEvaluates all the chained functions and give an array object , if the result is  already an array, it will provide as it is , if its single element, it converts to an array of single element        \n    \u003c/td\u003e\n    \u003ctd\u003e\n\u003cbr/\u003e\n\n    const res  = Optional.of([23,45])\n        .stream()\n        .map(i=\u003ei+1);\n\n    //returns [24,46]\n\n\u003cbr/\u003e\n\n    const res  = Optional.of(null)\n        .stream()\n        .map(i=\u003ei+1);\n\n    //returns []\n\n\u003cbr/\u003e\n\n\n\n    const res  = Optional.of(23)\n        .stream()\n        .map(i=\u003ei+1);\n\n    //returns [24]\n\n    \n\n\u003cbr/\u003e\n    \u003c/td\u003e\n\n\u003c/tr\u003e\n\n\u003ctr\u003e\n    \u003ctd\u003e getAsync \u003c/td\u003e\n    \u003ctd\u003e\n\u003cbr/\u003e\nEvaluate all the chained functions combined with promises give another Promise\u0026lt;result\u0026gt;  \n\n\u003cbr/\u003e\n\u003c/td\u003e\n    \u003ctd\u003e\n\n\n\u003cbr/\u003e\n\n\n\n     const result = await Optional.of(input)      \n                   .map(promiseFunctionToValidateUserDetails)\n                   .map(promiseFunctionToValidateRole)\n                    .map(regularFunctionToFormatData)\n                    .getAsync()\n                   \n\n\n\n\u003cbr/\u003e\n\n\n    \n    const result = await Optional.of('https://jsonplaceholder.typicode.com/todos/' + item)\n                        .map(fetch)\n                        .map(response =\u003e response.json())\n                        .filter(response =\u003e response.completed == true)\n                        .map(response =\u003e response.title)\n                        .getAsync();\n\n\n\u003cbr/\u003e\n\n**The below will also work fine**\n\n      const result = await Optional.of(21)\n                              .filter(val =\u003e val % 5 == 0)\n                              .map(val =\u003e val + 5)\n                              .getAsync()  \n\n      \n\n\u003cbr/\u003e\n    \u003c/td\u003e\n\n\u003c/tr\u003e\n\n\u003ctr\u003e\n    \u003ctd\u003e toAsync \u003c/td\u003e\n    \u003ctd\u003e\n\u003cbr/\u003e\nEvaluate all the chained functions combined with promises give another Promise\u0026lt;Optional\u0026gt;  which hold the response.\u003cbr/\u003e\n\u003cbr/\u003e\nAll the async based results must use toAsync and then they can use the Optional consolidation functions , get ,orElse,stream etc..\n\n\u003cbr/\u003e\n\u003c/td\u003e\n    \u003ctd\u003e\n\n\n\u003cbr/\u003e\n\n\n\n      Optional.of(input)      \n       .map(promiseFunctionToValidateUserDetails)\n       .map(promiseFunctionToValidateSomeOther)\n      .toAsync()\n       .then(optionalData=\u003e{\n            // optionalData.get() holds the result\n        })\n\n  \n\n\u003cbr/\u003e\n\n\n\n     const optionalData = await Optional.of(input)      \n                       .map(promiseFunctionToValidateUserDetails)\n                       .map(promiseFunctionToValidateSomeOther)\n                      .toAsync()\n\n    // optionalData.get() holds the result\n\n\n\n\n\n\u003cbr/\u003e\n    \u003c/td\u003e\n\n\u003c/tr\u003e\n\n\u003c/table\u003e\n\n###### Alternatives\n\nThere are some alternatives , Have a look into them as well\n\n [Optional.js](https://github.com/spencerwi/Optional.js)\n\n [amidevtech/optional.js](https://github.com/amidevtech/optional.js)\n\n\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmuthuishere%2Fdeclarative-optional","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmuthuishere%2Fdeclarative-optional","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmuthuishere%2Fdeclarative-optional/lists"}