
{"id":5285,"date":"2025-07-12T21:08:37","date_gmt":"2025-07-13T01:08:37","guid":{"rendered":"https:\/\/ikriv.com\/blog\/?p=5285"},"modified":"2025-07-12T21:08:37","modified_gmt":"2025-07-13T01:08:37","slug":"more-fun-with-recursive-types-in-typescript","status":"publish","type":"post","link":"https:\/\/ikriv.com\/blog\/?p=5285","title":{"rendered":"More fun with recursive types in Typescript"},"content":{"rendered":"<p>Typescript allows a type to reference another type defined further down in the same file, which can lead to rather interesting constructs.<\/p>\n<p>The vanilla circular reference fails:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\ntype Foo = Bar; \/\/ error: type alias Foo circularly references itself\r\ntype Bar = Foo; \/\/ error: type alias Bar circularly references itself\r\n<\/pre>\n<p>However, the following is legal, and surprisingly deep:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\ntype Foo = Bar;\r\ntype Bar = Foo&#x5B;];\r\n<\/pre>\n<p>What it defines is a recursive list of empty lists:<\/p>\n<ul>\n<li>The empty list <code>[]<\/code> is in the type.<\/li>\n<li>A list of any values already in the type is in the type.<\/li>\n<\/ul>\n<p>This is pretty close to <a href=\"https:\/\/proofwiki.org\/wiki\/Definition:Natural_Numbers\/Von_Neumann_Construction\">Von Neumann construction of natural numbers<\/a>:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n\/\/ combine two Foo lists\r\nfunction union(left: Foo, right: Foo) : Foo {\r\n  return &#x5B;...left, ...right];\r\n}\r\n\r\n\/\/ von Neumann definition of &quot;next number&quot;\r\nfunction succ(x: Foo) : Foo {\r\n  return union(x, &#x5B;x]);\r\n}\r\n\r\nconst zero: Foo = &#x5B;];\r\nconst one: Foo = succ(zero);        \/\/ &#x5B;&#x5B;]]\r\nconst two: Foo = succ(one);         \/\/ &#x5B;&#x5B;],&#x5B;&#x5B;]]]\r\nconst three: Foo = succ(two);       \/\/ &#x5B;&#x5B;],&#x5B;&#x5B;]],&#x5B;&#x5B;],&#x5B;&#x5B;]] ] ]\r\nconsole.log(JSON.stringify(three)); \/\/ prints &#x5B;&#x5B;],&#x5B;&#x5B;]],&#x5B;&#x5B;],&#x5B;&#x5B;]]]]\r\n<\/pre>\n<p>We can go ahead and implement addition and even multiplication of <code>Foo<\/code>s (but it&#8217;s going to be very slow):<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nfunction isZero(x: Foo) {\r\n  return x.length === 0;\r\n}\r\n\r\nfunction pred(x: Foo) : Foo {\r\n  if (isZero(x)) {\r\n    throw new Error(&quot;Zero has no predecessor&quot;);\r\n  }\r\n  return x.slice(0, -1); \/\/ all elements except the last one\r\n}\r\n\r\nfunction add(a: Foo, b: Foo): Foo {\r\n  if (isZero(b)) {\r\n    return a;\r\n  } else {\r\n    return succ(add(a, pred(b)));\r\n  }\r\n}\r\n\r\nfunction multiply(a: Foo, b: Foo): Foo {\r\n  if (isZero(b)) {\r\n    return zero;\r\n  } else {\r\n    return add(a, multiply(a, pred(b)));\r\n  }\r\n}\r\n\r\nconsole.log( JSON.stringify(add(one, two)));        \/\/ &#x5B;&#x5B;],&#x5B;&#x5B;]],&#x5B;&#x5B;],&#x5B;&#x5B;]]]]\r\nconsole.log( JSON.stringify(multiply(two, two)));   \/\/ &#x5B;&#x5B;],&#x5B;&#x5B;]],&#x5B;&#x5B;],&#x5B;&#x5B;]]],&#x5B;&#x5B;],&#x5B;&#x5B;]],&#x5B;&#x5B;],&#x5B;&#x5B;]]]]]\r\n<\/pre>\n<p>Recursive types are definitely fun.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Typescript allows a type to reference another type defined further down in the same file, which can lead to rather interesting constructs. The vanilla circular reference fails: type Foo = <a href=\"https:\/\/ikriv.com\/blog\/?p=5285\" class=\"more-link\">[&hellip;]<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"Layout":"","footnotes":""},"categories":[29],"tags":[],"class_list":["entry","author-ikriv","post-5285","post","type-post","status-publish","format-standard","category-typescript"],"_links":{"self":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/5285","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=5285"}],"version-history":[{"count":3,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/5285\/revisions"}],"predecessor-version":[{"id":5288,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/5285\/revisions\/5288"}],"wp:attachment":[{"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5285"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5285"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ikriv.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5285"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}