<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Eric&#39;s TILs</title>
  <subtitle>Short notes on things I learn day to day.</subtitle>
  <link href="https://limulus.net/tils/feed.xml" rel="self" />
  <link href="https://limulus.net/tils" />
  <updated>2025-11-01T20:45:00Z</updated>
  <id>https://limulus.net/tils</id>
  <author>
    <name>Eric McCarthy</name>
    <email>eric@limulus.net</email>
  </author>
  <entry>
    <title>Node.js Now Has TypeScript Type Stripping On By Default</title>
    <link href="https://limulus.net/tils/nodejs/type-stripping/" />
    <updated>2025-11-01T20:45:00Z</updated>
    <id>https://limulus.net/tils/nodejs/type-stripping/</id>
    <content type="html">&lt;p&gt;
  Despite keeping up better with my RSS feed reader lately, I still missed that &lt;a
    href=&quot;https://nodejs.org/en/blog/release/v22.18.0&quot;
  &gt;Node.js v22.18.0&lt;/a&gt; enables TypeScript &lt;a
    href=&quot;https://nodejs.org/en/learn/typescript/run-natively&quot;
  &gt;type stripping&lt;/a&gt; by default! Apparently it has also been enabled in v24 since its
  initial release.
&lt;/p&gt;
&lt;p&gt;
  This is great news, because it means that with some important caveats, it is now possible
  to run TypeScript natively in Node without needing to specify the &lt;code
  &gt;--experimental-strip-types&lt;/code&gt;
  flag. The caveats boil down to the fact that not every TypeScript language feature can be
  cleanly stripped to create valid JavaScript — a small handful of features require the
  TypeScript compiler to generate JavaScript code in order to fully function.
&lt;/p&gt;
&lt;p&gt;
  Luckily, in TypeScript v5.8 they introduced the &lt;a
    href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-5-8/#the---erasablesyntaxonly-option&quot;
  &gt;--erasableSyntaxOnly option&lt;/a&gt;. This raises errors when you use one of these features
  that would cause Node.js to throw a JS parse error even after type stripping.
&lt;/p&gt;
&lt;p&gt;
  The full list of these non-erasable language features is thankfully small, and, in my
  usage of TypeScript, quite manageable:
&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;namespace&lt;/code&gt;s and &lt;code&gt;module&lt;/code&gt;s with runtime code&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;import =&lt;/code&gt; aliases&lt;/li&gt;
  &lt;li&gt;
    Parameter properties in class constructors (the shorthand where &lt;code&gt;class {
      constructor(readonly foo: Bar) {} }&lt;/code&gt; auto-creates a &lt;code&gt;foo&lt;/code&gt; prop)
  &lt;/li&gt;
  &lt;li&gt;&lt;code&gt;enum&lt;/code&gt; declarations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
  I don’t think I’ve ever used executable code in a &lt;code&gt;namespace&lt;/code&gt; or a &lt;code
  &gt;module&lt;/code&gt; — the only time I use those is if I need to extend global or package types.
  I don’t think I’ve ever used
  &lt;code&gt;import =&lt;/code&gt; aliases. And I can easily live without parameter properties.
&lt;/p&gt;
&lt;p&gt;
  Lack of enums is probably the toughest pill to swallow. But the TypeScript docs for enums
  have a &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/enums.html#objects-vs-enums&quot;
  &gt;good suggestion&lt;/a&gt; — instead of creating an &lt;code&gt;enum&lt;/code&gt; create an object with
  &lt;code&gt;as const&lt;/code&gt;:
&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Direction&lt;/span&gt; = {
  &lt;span class=&quot;hljs-title class_&quot;&gt;Up&lt;/span&gt;: &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;,
  &lt;span class=&quot;hljs-title class_&quot;&gt;Right&lt;/span&gt;: &lt;span class=&quot;hljs-number&quot;&gt;2&lt;/span&gt;,
  &lt;span class=&quot;hljs-title class_&quot;&gt;Down&lt;/span&gt;: &lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt;,
  &lt;span class=&quot;hljs-title class_&quot;&gt;Left&lt;/span&gt;: &lt;span class=&quot;hljs-number&quot;&gt;4&lt;/span&gt;,
} &lt;span class=&quot;hljs-keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt;

&lt;span class=&quot;hljs-keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;DirectionEnum&lt;/span&gt; = (&lt;span class=&quot;hljs-keyword&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Direction&lt;/span&gt;)[keyof &lt;span class=&quot;hljs-keyword&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Direction&lt;/span&gt;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
  Hopefully &lt;a href=&quot;https://github.com/microsoft/TypeScript/issues/60790&quot;&gt;this proposal&lt;/a&gt;
  for an &lt;code&gt;as enum&lt;/code&gt; assertion gets some traction, since it would be nice to not
  need an extra &lt;code&gt;[Name]Enum&lt;/code&gt; type.
&lt;/p&gt;
&lt;p&gt;
  There are two other TS options that are helpful for running type-stripped TS in Node:
  &lt;code&gt;rewriteRelativeImportExtensions&lt;/code&gt; and &lt;code&gt;verbatimModuleSyntax&lt;/code&gt;. The
  former helps ensure you can use &lt;code&gt;.ts&lt;/code&gt; extensions in your &lt;code&gt;import&lt;/code&gt;
  statements, and the latter avoids surprises from top-level side-effects when &lt;code
  &gt;import&lt;/code&gt;ing a TS module.
&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Private Branches in Public Repos</title>
    <link href="https://limulus.net/tils/git/private-branches-in-public-repos/" />
    <updated>2025-10-11T21:47:00Z</updated>
    <id>https://limulus.net/tils/git/private-branches-in-public-repos/</id>
    <content type="html">&lt;p&gt;
  Something that’s semi-frequently bugged me about having this site be statically generated
  from flat files in a &lt;a href=&quot;https://github.com/limulus/limulus-dot-net/&quot;
  &gt;public GitHub repository&lt;/a&gt; is how to deal with drafts of posts. I want to be able to
  save my progress “in the cloud” so that I can pick it up on other machines without making
  anything half-baked publicly accessible.
&lt;/p&gt;
&lt;p&gt;
  The way I have been dealing with this is to use a persistent GitHub Codespace where
  changes can just sit untracked. But this means waiting for the Codespace to initialize
  when I start working on something, dealing with it pausing after inactivity, or even
  paying a bit for it if I go over GitHub’s free allotment. Without increasing the number of
  vCPUs on the Codespace, it’s also significantly slower than any machine I would use.
&lt;/p&gt;
&lt;p&gt;
  So a few times I’ve gone looking for a better solution to this problem and I’ve come up
  empty. But last night I decided to try again, and I gave &lt;a
    href=&quot;https://claude.ai/share/6c771cdd-77b1-4d6a-ba40-bba69f57d4b7&quot;
  &gt;this prompt&lt;/a&gt; to Claude:
&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;
    I wish I could have encrypted WIP branches in public GitHub repos. Does such a thing
    exist?
  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
  The response told me a bit about encryption options, but the first two alternatives it
  gave hints at something better:
&lt;/p&gt;
&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;
      Private branches - Just use a private repo for WIP, then move to public when ready
    &lt;/li&gt;
    &lt;li&gt;Fork workflow - Keep your fork private while the upstream is public&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;
  This made something click for me. If I created a private fork that only exists to store
  branches of work-in-progress, then I could add it as a remote to clones of the public
  repo. This would effectively let me have “private branches”! Finally!
&lt;/p&gt;
&lt;p&gt;
  I’ve implemented this by creating an empty private GitHub repo, added it as a remote named
  &lt;code&gt;private&lt;/code&gt;, pushed up everything from the original, and renamed the &lt;code
  &gt;origin&lt;/code&gt; remote to
  &lt;code&gt;public&lt;/code&gt;. This rename makes sure I will need to explicitly specify a remote
  when pushing new branches, preventing accidental pushes to the public repo.
&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;git remote add private https://github.com/limulus/limulus-dot-net-private.git
git push private --all
git remote rename origin public
git checkout -b my-wip
git push -u private my-wip
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
  When it comes time to merge my work into the public &lt;code&gt;main&lt;/code&gt; branch, I can do
  this easily with plain old &lt;code&gt;git merge&lt;/code&gt;. If there’s intermediate commits in the
  branch that I don’t want to be public I can always rebase or squash.
&lt;/p&gt;
&lt;p&gt;
  GitHub unfortunately does not track this separate private repo as a fork. That (plus the
  public/private dichotomy) means I can’t directly make PRs from the private repo. If I were
  using a PR based workflow, I expect that would have to reset the upstream of the branch to
  &lt;code&gt;public&lt;/code&gt; and then make a PR. But that’s not something I’m planning on doing
  with this project.
&lt;/p&gt;
&lt;p&gt;
  This is one of those things where I feel a little silly for not realizing this was
  possible. It’s obvious in retrospect that the ability to have multiple remotes in a
  repository would allow for this. But it’s a git feature I’ve used at most once before.
&lt;/p&gt;
</content>
  </entry>
</feed>