Hacker News new | past | comments | ask | show | jobs | submit login

I didn't get the "type-safe" part. How would it work in Go?

Let's say I have structs:

type User struct { ID TypeID }

type Post struct { ID TypeID }

How can I ensure the correct type is used in each of the structs?




It's not a language primitive. It's a data format that enables type safety in libraries or APIs (as opposed to a more opaque data format like UUIDv7.)


Any time you ever read a string, its type is always just going to be "string" (modulo whatever passes for a "string" in your programming language of choice). To get an actual non-string type, you'd need to parse that string, and presumably your parsing function would read the prefix and reject the string if it was passed an ID whose type doesn't match. So it's dynamically type-safe, if not statically type-safe.


I don't know go, but in C# I'd probably do something like the code below. The object really only needs to carry the uuid/guid, let the language type system worry about the difference between a user and post id. We just need a generic mechanism to ensure that UserId object can only be constructed from a valid type id string with type = user. For production use you'd obviously need more methods to construct it from a database tuple (mentioned elsewhere in the comments), etc.

    interface ITypeIdPrefix
    {
        static abstract string Prefix { get; }
    }

    abstract class TypeId<T>
        where T : TypeId<T>, ITypeIdPrefix, new()
    {
        public Guid Id { get; private init; }

        public override string ToString() => $"{T.Prefix}_{Id.ToBase32String()}";
        // Override GetHashcode(), Equals(), etc.

        public static bool TryParse(string s, out T? result)
        {
            if (!s.StartsWith(T.Prefix) || !TrySplitStringAndParseBase32ToGuid(s, out var id))
            {
                result = default;
                return false;
            }

            result = new T { Id = id };
            return true;
        }
    }

    class UserId : TypeId<UserId>, ITypeIdPrefix
    {
        public static string Prefix => "user";
    }

    class PostId : TypeId<PostId>, ITypeIdPrefix
    {
        public static string Prefix => "post";
    }


One way is to enforce in Marshal[0] and Unmarshal[1]

[0] https://pkg.go.dev/encoding/json#Marshaler

[1] https://pkg.go.dev/encoding/json#Unmarshaler


It’s stringly-typed type-safety: check if the value has the expected prefix.


This isn't about object types in any particular language.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: