Give our type aliases more specific type-level semantics.
(3) is nice because it gives type-level semantics to what would otherwise be plain type aliases. For example, even though FileId is represented as a string, it can't be just any string. It should only be a string that Memo vends. This lets us encode that in the type system:
workTree.remove("my-made-up-id")
^^^ type error
const fileId = workTree.fileIdForPath("my-file.txt")
workTree.remove(fileId)
^^^ just fine
but it also gets erased at compile time so it doesn't change runtime semantics.
(3) is nice because it gives type-level semantics to what would otherwise be plain type aliases. For example, even though
FileId
is represented as astring
, it can't be just anystring
. It should only be astring
that Memo vends. This lets us encode that in the type system:but it also gets erased at compile time so it doesn't change runtime semantics.