{"id":1129,"date":"2023-03-24T07:00:00","date_gmt":"2023-03-24T06:00:00","guid":{"rendered":"https:\/\/greladesign.co\/blog\/?p=1129"},"modified":"2023-03-24T12:48:02","modified_gmt":"2023-03-24T11:48:02","slug":"typescript-add-typing-to-the-lodash-entries-helper","status":"publish","type":"post","link":"https:\/\/greladesign.co\/blog\/2023\/03\/24\/typescript-add-typing-to-the-lodash-entries-helper\/","title":{"rendered":"TypeScript: Add typing to the lodash entries helper"},"content":{"rendered":"\n<p>This is a followup post to the other one where we&#8217;ve added types to the <a href=\"https:\/\/greladesign.co\/blog\/2023\/03\/23\/typescript-add-typing-to-the-lodash-keys-helper\/\">lodash keys helper<\/a>. This time we will add types to the entries helper, which is an <a href=\"https:\/\/lodash.com\/docs#toPairs\">alias to the toPairs helper<\/a>, where we can also benefit from the proper typing of the returned list of key, value pairs. Let&#8217;s start with the same setup as in the other post:<\/p>\n\n\n\n<!--more-->\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"TypeScript\" data-shcb-language-slug=\"typescript\"><span><code class=\"hljs language-typescript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> { entries } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"lodash\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/ test object upon which we will call keys<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">type<\/span> TObject = {\n<\/span><\/span><span class='shcb-loc'><span>  fieldNum: <span class=\"hljs-built_in\">number<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  fieldStr: <span class=\"hljs-built_in\">string<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  fieldBln: <span class=\"hljs-built_in\">boolean<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  fieldArr: <span class=\"hljs-built_in\">string<\/span>&#91;];\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> o: TObject = {\n<\/span><\/span><span class='shcb-loc'><span>  fieldNum: <span class=\"hljs-number\">42<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>  fieldStr: <span class=\"hljs-string\">'greladesign'<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>  fieldBln: <span class=\"hljs-literal\">true<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>  fieldArr: &#91;<span class=\"hljs-string\">\"a\"<\/span>, <span class=\"hljs-string\">\"b\"<\/span>, <span class=\"hljs-string\">\"c\"<\/span>]\n<\/span><\/span><span class='shcb-loc'><span>};\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/ tooltip will show: const entryPairs: &#91;string, string | number | boolean | string&#91;]]&#91;]<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> entryPairs = entries(o);\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>entryPairs.forEach((pair <span class=\"hljs-comment\">\/* : &#91;string, string | number | boolean | string&#91;]] *\/<\/span>): <span class=\"hljs-function\"><span class=\"hljs-params\">void<\/span> =&gt;<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-comment\">\/\/ You have lost the information about keys of the object, <\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-comment\">\/\/ TypeScript will no longer be able to verify field for you<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">if<\/span> (pair&#91;<span class=\"hljs-number\">0<\/span>] === <span class=\"hljs-string\">'fieldBoolean'<\/span>) { <span class=\"hljs-comment\">\/\/ TS will accept this incorrect field name<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> flag = pair&#91;<span class=\"hljs-number\">1<\/span>] <span class=\"hljs-comment\">\/\/ TS will use a union of all value types it found on object<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-comment\">\/\/ which is string, string | number | boolean | string&#91;]<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>})\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">TypeScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">typescript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Sorting out the type of <code>pair[0]<\/code> is fairly simple to resolve:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/ add following to your typings e.g. lodash-override.d.ts<\/span>\n<\/span><\/span><span class='shcb-loc'><span>export {};\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">declare<\/span> module <span class=\"hljs-string\">'lodash'<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-class\"><span class=\"hljs-keyword\">interface<\/span> <span class=\"hljs-title\">LoDashStatic<\/span> <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>    toPairs&lt;O, K extends keyof O = keyof O&gt;(object?: O): &#91;K, O&#91;K]]&#91;];\n<\/span><\/span><span class='shcb-loc'><span>    entries&lt;O, K extends keyof O = keyof O&gt;(object?: O): &#91;K, O&#91;K]]&#91;];\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This helps a bit, keys are correctly picked up from the object<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"TypeScript\" data-shcb-language-slug=\"typescript\"><span><code class=\"hljs language-typescript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/ tooltip will now show: const entryPairs: &#91;keyof TObject, string | number | boolean | string&#91;]]&#91;]<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> entryPairs = entries(o); \n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>entryPairs.forEach((pair <span class=\"hljs-comment\">\/* : &#91;keyof TObject, string | number | boolean | string&#91;]] *\/<\/span>): <span class=\"hljs-function\"><span class=\"hljs-params\">void<\/span> =&gt;<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-comment\">\/\/ TS will now correctly identify this as an error<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-comment\">\/\/ if (pair&#91;0] === 'fieldBoolean'){ <\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-comment\">\/\/ After correcting the field name we're good<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">if<\/span> (pair&#91;<span class=\"hljs-number\">0<\/span>] === <span class=\"hljs-string\">'fieldBln'<\/span>) {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> flag = pair&#91;<span class=\"hljs-number\">1<\/span>] <span class=\"hljs-comment\">\/\/ This still is a union of all value types<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>})\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">TypeScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">typescript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The problem is still with the value of the pair. TypeScript doesnt know how to map it and it takes all of them as union. We need to let TS know how to infer the type by the used key. <br \/><br \/>Let&#8217;s prepare a helper type that will produce correctly typed tuple.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"TypeScript\" data-shcb-language-slug=\"typescript\"><span><code class=\"hljs language-typescript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-keyword\">type<\/span> TTuple&lt;O&gt; = {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-comment\">\/* map the possible tuples against object's field *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  &#91;Field <span class=\"hljs-keyword\">in<\/span> keyof O]: &#91;field: Field, value: O&#91;Field]];\n<\/span><\/span><span class='shcb-loc'><span>}<span class=\"hljs-comment\">\/* select the proper tuple *\/<\/span>&#91;keyof O];\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> tuple: TTuple&lt;TObject&gt; = &#91;<span class=\"hljs-string\">'fieldBln'<\/span>, <span class=\"hljs-literal\">true<\/span>];\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/ tooltip over tuple shows const tuple: &#91;field: \"fieldBln\", value: boolean]<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> key = tuple&#91;<span class=\"hljs-number\">0<\/span>];\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> value = tuple&#91;<span class=\"hljs-number\">1<\/span>];\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">TypeScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">typescript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We have a helper that will select correct type for the value based on the selected field. <br \/><br \/>Let&#8217;s adjust the lodash typing:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"TypeScript\" data-shcb-language-slug=\"typescript\"><span><code class=\"hljs language-typescript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/ add following to your typings e.g. lodash-override.d.ts<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">export<\/span> {};\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">type<\/span> TTuple&lt;O&gt; = {\n<\/span><\/span><span class='shcb-loc'><span>  &#91;Field <span class=\"hljs-keyword\">in<\/span> keyof O]: &#91;field: Field, value: O&#91;Field]];\n<\/span><\/span><span class='shcb-loc'><span>}&#91;keyof O];\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">declare<\/span> <span class=\"hljs-keyword\">module<\/span> 'lodash' {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">interface<\/span> LoDashStatic {\n<\/span><\/span><span class='shcb-loc'><span>    toPairs&lt;O&gt;(object?: O): TTuple&lt;O&gt;&#91;]; \n<\/span><\/span><span class='shcb-loc'><span>    entries&lt;O&gt;(object?: O): TTuple&lt;O&gt;&#91;]; \n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">TypeScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">typescript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now we&#8217;re all set and you can have properly typed entries. I hope you&#8217;ll find it useful.<\/p>\n\n\n\n<h3>Source code<\/h3>\n\n\n\n<ul class=\"wp-container-6 wp-block-social-links has-large-icon-size is-style-pill-shape\"><li class=\"wp-social-link wp-social-link-github wp-block-social-link\"><a href=\"https:\/\/github.com\/LukaszGrela\/blog-code-typescript-add-typing-to-the-lodash\" aria-label=\"Source code\" rel=\"noopener nofollow\" target=\"_blank\" class=\"wp-block-social-link-anchor\"> <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" version=\"1.1\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" role=\"img\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z\"><\/path><\/svg><\/a><\/li><\/ul>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is a followup post to the other one where we&#8217;ve added types to the lodash keys helper. This time we will add types to the entries helper, which is an alias to the toPairs helper, where we can also &hellip; <a href=\"https:\/\/greladesign.co\/blog\/2023\/03\/24\/typescript-add-typing-to-the-lodash-entries-helper\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_import_markdown_pro_load_document_selector":0,"_import_markdown_pro_submit_text_textarea":""},"categories":[356],"tags":[357],"_links":{"self":[{"href":"https:\/\/greladesign.co\/blog\/wp-json\/wp\/v2\/posts\/1129"}],"collection":[{"href":"https:\/\/greladesign.co\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/greladesign.co\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/greladesign.co\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/greladesign.co\/blog\/wp-json\/wp\/v2\/comments?post=1129"}],"version-history":[{"count":10,"href":"https:\/\/greladesign.co\/blog\/wp-json\/wp\/v2\/posts\/1129\/revisions"}],"predecessor-version":[{"id":1148,"href":"https:\/\/greladesign.co\/blog\/wp-json\/wp\/v2\/posts\/1129\/revisions\/1148"}],"wp:attachment":[{"href":"https:\/\/greladesign.co\/blog\/wp-json\/wp\/v2\/media?parent=1129"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/greladesign.co\/blog\/wp-json\/wp\/v2\/categories?post=1129"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/greladesign.co\/blog\/wp-json\/wp\/v2\/tags?post=1129"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}