{"id":16793925,"url":"https://github.com/robertmassaioli/ts-is-present","last_synced_at":"2025-04-09T23:18:41.543Z","repository":{"id":42704699,"uuid":"289613195","full_name":"robertmassaioli/ts-is-present","owner":"robertmassaioli","description":"Solves the TypeScript filtering of undefined and null issue, amongst others: https://github.com/microsoft/TypeScript/issues/16069#issuecomment-565658443","archived":false,"fork":false,"pushed_at":"2023-01-07T04:55:05.000Z","size":285,"stargazers_count":66,"open_issues_count":4,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-09T23:18:36.249Z","etag":null,"topics":["optionals","presence","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/robertmassaioli.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-08-23T04:09:53.000Z","updated_at":"2025-04-08T14:08:29.000Z","dependencies_parsed_at":"2023-02-06T12:30:49.272Z","dependency_job_id":null,"html_url":"https://github.com/robertmassaioli/ts-is-present","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertmassaioli%2Fts-is-present","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertmassaioli%2Fts-is-present/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertmassaioli%2Fts-is-present/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertmassaioli%2Fts-is-present/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robertmassaioli","download_url":"https://codeload.github.com/robertmassaioli/ts-is-present/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248125592,"owners_count":21051771,"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","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":["optionals","presence","typescript"],"created_at":"2024-10-13T08:51:02.924Z","updated_at":"2025-04-09T23:18:41.524Z","avatar_url":"https://github.com/robertmassaioli.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ts-is-present\n\nThe `ts-is-present` package provides common functions to let you filter out the `null` or `undefined`\nvalues from arrays in your code AND end up with the types that you expect.\n\n## Super short explanation\n\nInstall: `npm install --save ts-is-present`\n\n``` typescript\nimport { isPresent, isDefined, isFilled } from 'ts-is-present';\n\narrayWithUndefinedAndNullValues.filter(isPresent)\narrayWithUndefinedValues.filter(isDefined)\narrayWithNullValues.filter(isFilled)\n```\n\nIn a nutshell:\n\n - `isPresent`: Removes `undefined` and `null` values via a `filter`.\n - `isDefined`: Removes `undefined` values via a `filter`.\n - `isFilled`: Removes `null` values via a `filter`.\n - `hasPresentKey`: Removes everything that is not an object with the expected key present via a `filter`.\n - `hasValueAtKey`: The same as `hasPresentKey` but with an additional check for a particular value. \n\n## Short explanation\n\nThe following code feels like it should type check, but it does not:\n\n![Failing code](https://i.imgur.com/d8EBtg6.png)\n\nIt fails because the TypeScript type checker can't intuit that the lambda function eliminates the undefined values:\n\n![Reasons for failing code](https://i.imgur.com/32biELe.png)\n\nThis library provides the three `isPresent`, `isDefined` and `isFilled` functions to solve this issue in the way that you would\nexpect the `filter` function to work:\n\n![Working code](https://i.imgur.com/WqgHTrU.png)\n\nUse this library to dramatically simplify your TypeScript code and get the full power of your types.\n\n## Use `isPresent` to drop all `Nothing` values\n\nThe `isDefined` and `isFilled` functions are only useful if you want `null` or `undefined` results to remain respectively\nafter you have performed some filtering operations. However, `isPresent` filters any values that represent nothing\nfrom your results (`null`, `undefined` or `void`), like so:\n\n``` typescript\nimport { isPresent } from 'ts-is-present';\n\ntype TestData = {\n  data: string;\n};\n\nfunction getVoid(): void {\n  return undefined;\n}\n\nconst results: Array\u003cTestData | undefined | null | void\u003e = [\n  { data: 'hello' },\n  undefined,\n  { data: 'world' },\n  getVoid(),\n  null,\n  { data: 'wow' },\n\n];\n\nconst definedResults: Array\u003cTestData\u003e = results.filter(isPresent);\n```\n\nAs you can see, `isPresent` can drop `undefined`, `null` and `void` values from an array (where `void` values are\nreally just `undefined` in disguise). This makes it broadly applicable.\n\n## Use `hasPresentKey` and `hasValueAtKey` to filter objects\n\nIf you want to find all of the objects in an array that have a particular field present, you can use `hasPresentKey`. For example:\n\n``` typescript\nconst filesWithUrl = files.filter(hasPresentKey(\"url\"));\n files[0].url // TS will know that this is present\n```\n\nIf you want to find all of the objects with a particular field set to a particular value you can use `hasValueAtKey`:\n\n``` typescript\ntype File = { type: \"image\", imageUrl: string } | { type: \"pdf\", pdfUrl: string };\nconst files: File[] = \u003csome data here\u003e;\n\nconst filesWithUrl = files.filter(hasValueKey(\"type\", \"image\" as const));\nfiles[0].type // TS will now know that this is \"image\"\n```\n\nThese functions are useful in filtering out objects from arrays.\n\n## Deeper Explanation\n\nAn example of the fundamental problem can be [found in the TypeScript bug tracker](https://github.com/microsoft/TypeScript/issues/16069) \nbut we will try and explain it again simply here.\n\nFirstly, TypeScript can not look at the following \nlambda function `x =\u003e x !== undefined` and derive the type `(t: T | undefined): t is T`. \nInstead, the best it can do is to derive the type: `(t: any): boolean`.\n\nSecondly, TypeScript has two type definitions for the `filter` function. They are:\n\n``` typescript\n// Definition 1\nfilter\u003cS extends T\u003e(callbackfn: (value: T, index: number, array: T[]) =\u003e value is S, thisArg?: any): S[];\n    \n// Definition 2\nfilter(callbackfn: (value: T, index: number, array: T[]) =\u003e unknown, thisArg?: any): T[];\n```\n\nIf we look at those types carefully they differ in an interesting way. \n\nThe second definition expects a callback function where the return type of that callback is `unknown`; \nthis will be treated as a truthy value when the filtering is performed. Most importantly, in this \nfunction, if you give it an `Array\u003cT\u003e` then you will get back an `Array\u003cT\u003e`; even if the lambda \nthat you provided \"proves\" that the type could be restricted further.\n\nThe first definition, however, expects that the return type of the callback will be `value is S` \nwhere the generic definition of `S extends T` applies. This means that, if you give this version of \nfilter an `Array\u003cT\u003e` and a function that can tell if a particular `T` is actually of the more restrictive \ntype `S` then it will give you back an `Array\u003cS\u003e`. This is the critical feature of the `filter` type definitions\nthat lets the functions defined in this library refine the types inside a filter.\n\nIn short, when you write the following code the second `filter` definition is used:\n\n``` typescript\nresults.filter(x =\u003e x !== undefined)\n```\n\nHowever, when you use this library the first `filter` definition is used:\n\n``` typescript\nresults.filter(isDefined)\n```\n\nThat is why this library helps you derive the types you expect.\n\n## Contributors\n\n - [Jack Tomaszewski](https://github.com/jtomaszewski)\n - [Robert Massaioli](https://github.com/robertmassaioli) (Maintainer) \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobertmassaioli%2Fts-is-present","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobertmassaioli%2Fts-is-present","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobertmassaioli%2Fts-is-present/lists"}