{"id":240,"date":"2018-01-02T17:38:00","date_gmt":"2018-01-02T14:38:00","guid":{"rendered":"https:\/\/iamakulov.com\/notes\/?p=240"},"modified":"2018-12-10T01:51:02","modified_gmt":"2018-12-09T22:51:02","slug":"apis-arrays","status":"publish","type":"post","link":"https:\/\/iamakulov.com\/notes\/apis-arrays\/","title":{"rendered":"Designing APIs: use arrays instead of rest parameters"},"content":{"rendered":"<p><!--\n\nGOT A SECOND EXAMPLE! THE reduce-reducers LIBRARY ACCEPTS REDUCERS AS REST ARGUMENTS. But at some point, they needed to add a support for initialState, and they didn\u2019t have another option but to add it as a last argument and write complex checks like `typeof arguments[arguments.length - 1]`. Such pattern can\u2019t even be supported by the JS syntax: in JS, rest arguments should come last.\n\n--><\/p>\n<p>Or why <nobr><code>_.get(object, ['deep', 'prop'])<\/code><\/nobr> is better than <nobr><code>_.get(object, 'deep', 'prop')<\/code><\/nobr>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2018\/01\/2018-01-02_17-42-15.png\" alt=\"\" width=\"1920\" height=\"1080\" class=\"alignnone size-full wp-image-907\" srcset=\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2018\/01\/2018-01-02_17-42-15.png 1920w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2018\/01\/2018-01-02_17-42-15-300x169.png 300w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2018\/01\/2018-01-02_17-42-15-768x432.png 768w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2018\/01\/2018-01-02_17-42-15-1024x576.png 1024w, https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2018\/01\/2018-01-02_17-42-15-1200x675.png 1200w\" sizes=\"(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/p>\n<p>Imagine you\u2019re designing a function that accepts a variable number of arguments. Like <code>getDeepProperty()<\/code>:<\/p>\n<pre><code >function getDeepProperty(&lt;object&gt; and &lt;chain of nested fields&gt;) {\r\n  \/\/ Retrieve the deep property and return it\r\n}\r\n<\/code><\/pre>\n<p>And you\u2019re facing a choice: should you pass the chain of fields directly:<\/p>\n<pre><code >function getDeepProperty(object, ...fields) { \/* ... *\/ }\r\n\r\ngetDeepProperty(someData, 'i', 'love', 'designing', 'apis');\r\n<\/code><\/pre>\n<p>or as an array:<\/p>\n<pre><code >function getDeepProperty(object, fields) { \/* ... *\/ }\r\n\r\ngetDeepProperty(someData, ['i', 'love', 'designing', 'apis']);\r\n<\/code><\/pre>\n<p>Earlier, I preferred the former way because it seemed \u201ccleaner\u201d. Now, I use the latter one \u2013 because it makes the function forward-compatible. <\/p>\n<p>At some moment in the future, you might want to pass an additional parameter specifying the default return value \u2013 i.e., what the function should return if the deep property is absent. The only backwards-compatible way to do this is to pass this parameter as the last argument to the function, like this:<\/p>\n<pre><code >function getDeepProperty(\r\n  &lt;object&gt;,\r\n  &lt;chain of fields&gt;,\r\n  &lt;default return value&gt;\r\n) {\r\n  \/\/ Retrieve the deep property and return it,\r\n  \/\/ or return the default value\r\n}\r\n<\/code><\/pre>\n<p>And here comes the difference. If your function accepts an array, you just append a new argument, do a <code>switch (arguments.length)<\/code> inside your function body, and the old code keeps working:<\/p>\n<pre><code >function getDeepProperty(...args) {\r\n  if (args.length === 3) {\r\n    const [object, fields, defaultValue] = args;\r\n    \/\/ Execute the new behavior\r\n  } else {\r\n    const [object, fields] = args;\r\n    \/\/ Execute the old behavior\r\n  }\r\n}\r\n\r\n\/\/ The new API calls work great: a user passes three arguments,\r\n\/\/ the function understands this and returns 'no'\r\ngetDeepProperty({}, ['i', 'love', 'designing', 'apis'], 'no');\r\n\r\n\/\/ The old API calls keep working: a user passes two arguments,\r\n\/\/ the function works as usual and e.g. throws an exception\r\ngetDeepProperty({}, ['i', 'love', 'designing', 'apis']);\r\n<\/code><\/pre>\n<p>But if your function accepts a variable number of arguments, you become unable to add an argument without breaking the old API users. The function can\u2019t understand whether the last parameter is the default return value or a value in the chain of fields:<\/p>\n<pre><code >function getDeepProperty(object, ...fieldsAndDefaultValue) {\r\n  \/\/ So, is fieldsAndDefaultValue[fieldsAndDefaultValue.length - 1]\r\n  \/\/ a default value or just a field in a chain of fields?\r\n  \/\/ You can\u2019t know\r\n\r\n  \/\/ Execute the new behavior\r\n}\r\n\r\n\/\/ The new API works great: the function returns 'no'\r\ngetDeepProperty({}, 'i', 'love', 'designing', 'apis', 'no');\r\n\r\n\/\/ But the old API breaks: the function returns 'apis'\r\ngetDeepProperty({}, 'i', 'love', 'designing', 'apis');\r\n<\/code><\/pre>\n<p>So, here\u2019s the rule:<\/p>\n<div class='large-text'>When defining a function, prefer arrays over variable number of arguments<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Or why _.get(object, [&#8216;deep&#8217;, &#8216;prop&#8217;]) is better than _.get(object, &#8216;deep&#8217;, &#8216;prop&#8217;). Imagine you\u2019re designing a function that accepts a variable number of arguments. Like getDeepProperty(): And you\u2019re facing a choice: should you pass the chain of fields directly: or as an array: Earlier, I preferred the former way because it seemed \u201ccleaner\u201d. Now, I use &hellip; <a href=\"https:\/\/iamakulov.com\/notes\/apis-arrays\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Designing APIs: use arrays instead of rest parameters&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[19,6],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v23.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Designing APIs: use arrays instead of rest parameters - Ivan Akulov\u2019s blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/iamakulov.com\/notes\/apis-arrays\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Designing APIs: use arrays instead of rest parameters\" \/>\n<meta property=\"og:description\" content=\"Or why _.get(object, [&#039;deep&#039;, &#039;prop&#039;]) is better than _.get(object, &#039;deep&#039;, &#039;prop&#039;)\" \/>\n<meta property=\"og:url\" content=\"https:\/\/iamakulov.com\/notes\/apis-arrays\/\" \/>\n<meta property=\"og:site_name\" content=\"Ivan Akulov\u2019s blog\" \/>\n<meta property=\"article:publisher\" content=\"http:\/\/facebook.com\/iamakulov.page\" \/>\n<meta property=\"article:published_time\" content=\"2018-01-02T14:38:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2018-12-09T22:51:02+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2018\/01\/2018-01-02_17-32-28-2.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1920\" \/>\n\t<meta property=\"og:image:height\" content=\"1008\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Ivan Akulov\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:title\" content=\"Designing APIs: use arrays instead of rest parameters\" \/>\n<meta name=\"twitter:description\" content=\"Or why _.get({}, [&#039;deep&#039;, &#039;prop&#039;]) is better than _.get({}, &#039;deep&#039;, &#039;prop&#039;)\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2018\/01\/2018-01-02_17-32-28.png\" \/>\n<meta name=\"twitter:creator\" content=\"@iamakulov\" \/>\n<meta name=\"twitter:site\" content=\"@iamakulov\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Ivan Akulov\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"2 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/iamakulov.com\/notes\/apis-arrays\/\",\"url\":\"https:\/\/iamakulov.com\/notes\/apis-arrays\/\",\"name\":\"Designing APIs: use arrays instead of rest parameters - Ivan Akulov\u2019s blog\",\"isPartOf\":{\"@id\":\"https:\/\/iamakulov.com\/notes\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/iamakulov.com\/notes\/apis-arrays\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/iamakulov.com\/notes\/apis-arrays\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2018\/01\/2018-01-02_17-42-15.png\",\"datePublished\":\"2018-01-02T14:38:00+00:00\",\"dateModified\":\"2018-12-09T22:51:02+00:00\",\"author\":{\"@id\":\"https:\/\/iamakulov.com\/notes\/#\/schema\/person\/ebf7b61bf573e7be5fe438f50ebd9b81\"},\"breadcrumb\":{\"@id\":\"https:\/\/iamakulov.com\/notes\/apis-arrays\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/iamakulov.com\/notes\/apis-arrays\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/iamakulov.com\/notes\/apis-arrays\/#primaryimage\",\"url\":\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2018\/01\/2018-01-02_17-42-15.png\",\"contentUrl\":\"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2018\/01\/2018-01-02_17-42-15.png\",\"width\":1920,\"height\":1080},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/iamakulov.com\/notes\/apis-arrays\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/iamakulov.com\/notes\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Designing APIs: use arrays instead of rest parameters\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/iamakulov.com\/notes\/#website\",\"url\":\"https:\/\/iamakulov.com\/notes\/\",\"name\":\"Ivan Akulov\u2019s blog\",\"description\":\"Ivan Akulov writes about his front-end experience, React, webpack, and performance optimizations.\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/iamakulov.com\/notes\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/iamakulov.com\/notes\/#\/schema\/person\/ebf7b61bf573e7be5fe438f50ebd9b81\",\"name\":\"Ivan Akulov\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/iamakulov.com\/notes\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/f68e4cd477cef1577c339e6f09736d3a?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/f68e4cd477cef1577c339e6f09736d3a?s=96&d=mm&r=g\",\"caption\":\"Ivan Akulov\"},\"description\":\"I'm a software engineer specializing in web performance, JavaScript, and React. I\u2019m also a Google Developer Expert. I work at Framer.\",\"sameAs\":[\"http:\/\/iamakulov.com\",\"https:\/\/x.com\/iamakulov\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Designing APIs: use arrays instead of rest parameters - Ivan Akulov\u2019s blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/iamakulov.com\/notes\/apis-arrays\/","og_locale":"en_US","og_type":"article","og_title":"Designing APIs: use arrays instead of rest parameters","og_description":"Or why _.get(object, ['deep', 'prop']) is better than _.get(object, 'deep', 'prop')","og_url":"https:\/\/iamakulov.com\/notes\/apis-arrays\/","og_site_name":"Ivan Akulov\u2019s blog","article_publisher":"http:\/\/facebook.com\/iamakulov.page","article_published_time":"2018-01-02T14:38:00+00:00","article_modified_time":"2018-12-09T22:51:02+00:00","og_image":[{"width":1920,"height":1008,"url":"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2018\/01\/2018-01-02_17-32-28-2.png","type":"image\/png"}],"author":"Ivan Akulov","twitter_card":"summary_large_image","twitter_title":"Designing APIs: use arrays instead of rest parameters","twitter_description":"Or why _.get({}, ['deep', 'prop']) is better than _.get({}, 'deep', 'prop')","twitter_image":"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2018\/01\/2018-01-02_17-32-28.png","twitter_creator":"@iamakulov","twitter_site":"@iamakulov","twitter_misc":{"Written by":"Ivan Akulov","Est. reading time":"2 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/iamakulov.com\/notes\/apis-arrays\/","url":"https:\/\/iamakulov.com\/notes\/apis-arrays\/","name":"Designing APIs: use arrays instead of rest parameters - Ivan Akulov\u2019s blog","isPartOf":{"@id":"https:\/\/iamakulov.com\/notes\/#website"},"primaryImageOfPage":{"@id":"https:\/\/iamakulov.com\/notes\/apis-arrays\/#primaryimage"},"image":{"@id":"https:\/\/iamakulov.com\/notes\/apis-arrays\/#primaryimage"},"thumbnailUrl":"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2018\/01\/2018-01-02_17-42-15.png","datePublished":"2018-01-02T14:38:00+00:00","dateModified":"2018-12-09T22:51:02+00:00","author":{"@id":"https:\/\/iamakulov.com\/notes\/#\/schema\/person\/ebf7b61bf573e7be5fe438f50ebd9b81"},"breadcrumb":{"@id":"https:\/\/iamakulov.com\/notes\/apis-arrays\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/iamakulov.com\/notes\/apis-arrays\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/iamakulov.com\/notes\/apis-arrays\/#primaryimage","url":"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2018\/01\/2018-01-02_17-42-15.png","contentUrl":"https:\/\/iamakulov.com\/notes2\/wp-content\/uploads\/2018\/01\/2018-01-02_17-42-15.png","width":1920,"height":1080},{"@type":"BreadcrumbList","@id":"https:\/\/iamakulov.com\/notes\/apis-arrays\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/iamakulov.com\/notes\/"},{"@type":"ListItem","position":2,"name":"Designing APIs: use arrays instead of rest parameters"}]},{"@type":"WebSite","@id":"https:\/\/iamakulov.com\/notes\/#website","url":"https:\/\/iamakulov.com\/notes\/","name":"Ivan Akulov\u2019s blog","description":"Ivan Akulov writes about his front-end experience, React, webpack, and performance optimizations.","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/iamakulov.com\/notes\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/iamakulov.com\/notes\/#\/schema\/person\/ebf7b61bf573e7be5fe438f50ebd9b81","name":"Ivan Akulov","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/iamakulov.com\/notes\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/f68e4cd477cef1577c339e6f09736d3a?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/f68e4cd477cef1577c339e6f09736d3a?s=96&d=mm&r=g","caption":"Ivan Akulov"},"description":"I'm a software engineer specializing in web performance, JavaScript, and React. I\u2019m also a Google Developer Expert. I work at Framer.","sameAs":["http:\/\/iamakulov.com","https:\/\/x.com\/iamakulov"]}]}},"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/posts\/240"}],"collection":[{"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/comments?post=240"}],"version-history":[{"count":32,"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/posts\/240\/revisions"}],"predecessor-version":[{"id":1393,"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/posts\/240\/revisions\/1393"}],"wp:attachment":[{"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/media?parent=240"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/categories?post=240"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/tags?post=240"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}