Live data from Hacker News

Adding type safety to object IDs in TypeScript

kravchyk.com

1–10 of 61 posts

Re: Adding type safety to object IDs in TypeScript

#5
This is pretty close to type branding (newtype wrapping for the Haskell-inclined), though using template literal types is pretty novel. Normal brands look something like this:

    type Brand = BaseType & { readonly __brand__: Brand };
    type FooId = Brand;
    function fooBar(asdf: FooId | 'foobar'): void { }
fooBar will only accept the literal string 'foobar' or a true FooId, but not any arbitrary string. FooId would then come from a function that validates strings as FooIds, or some other part of the app that is an authoritative source for them. Brands extend their BaseType so they can be used anywhere their BaseType is used, but not the inverse

Re: Adding type safety to object IDs in TypeScript

#7
post #5

This is pretty close to type branding (newtype wrapping for the Haskell-inclined), though using template literal types is pretty novel. Normal brands look something like this: type Brand = BaseType & { readonly __brand__: Brand }; type FooId = Brand ; function fooBar(asdf: FooId | 'foobar'): void { } fooBar will only accept the literal string 'foobar' or a true FooId, but not any arbitrary string. FooId would then co…

I prefer the Brand solution, it works well for existing ID sets that you can't easily migrate to have an actual string prefix

Re: Adding type safety to object IDs in TypeScript

#8
post #5

This is pretty close to type branding (newtype wrapping for the Haskell-inclined), though using template literal types is pretty novel. Normal brands look something like this: type Brand = BaseType & { readonly __brand__: Brand }; type FooId = Brand ; function fooBar(asdf: FooId | 'foobar'): void { } fooBar will only accept the literal string 'foobar' or a true FooId, but not any arbitrary string. FooId would then co…

The meaning is different though. Brands convey intent, UserId brand would allow only other UserId brands, but with string literal types "any" type that matches 'user_${string}' will do

Re: Adding type safety to object IDs in TypeScript

#9
post #5

This is pretty close to type branding (newtype wrapping for the Haskell-inclined), though using template literal types is pretty novel. Normal brands look something like this: type Brand = BaseType & { readonly __brand__: Brand }; type FooId = Brand ; function fooBar(asdf: FooId | 'foobar'): void { } fooBar will only accept the literal string 'foobar' or a true FooId, but not any arbitrary string. FooId would then co…

If you want to make this easier to keep private between encapsulation boundaries the additional suggestion is make sure the Brand type extends symbol:

    type Brand = BaseType & { readonly __brand__: Brand };
    const FooIdBrand = Symbol('FooId');
    type FooId = Brand;
    function fooBar(asdf: FooId | 'foobar'): void { }
Using a private shared symbol your authoritative validation/sources can share your brand symbol and no one else can create one without using your validation. Private symbol brands in this way become the closest Typescript gets to "nominal types".
Post reply on HN