5 ms·
> In all scenarios [...] there is no reason for wanting nominal typing. Hard disagree. It's very useful to e.g. make a `PasswordResetToken` be different from
by kaoD 2y ago
> In all scenarios [...] there is no reason for wanting nominal typing.
Hard disagree.
It's very useful to e.g. make a `PasswordResetToken` be different from a `CsrfToken`.
Prepending a template literal changes the underlying value and you can no longer do stuff like `Buffer.from(token, 'base64')`. It's just a poor-man's version of branding with all the disadvantages and none of the advantages.
You can still `hash.toUpperCase()` a branded type. It just stops being branded (as it should) just like `toUpperCase` with `hashed_` prepended would stop working... except `toLowerCase()` would completely pass your template literal check while messing with the uppercase characters in the token (thus it should no longer be a token, i.e. your program is now wrong).
Additionally branded types can have multiple brands[0] that will work as you expect.
So a user id from your DB can be a `UserId`, a `ModeratorId`, an `AdminId` and a plain string (when actually sending it to a raw DB method) as needed.
Try doing this (playground in [1]) with template literals:
type UserId = Tagged<string, 'UserId'>
type ModeratorId = Tagged<UserId, 'ModeratorId'> // notice we composed with UserId here
type AdminId = Tagged<UserId, 'AdminId'> // and here
const banUser = (banned: UserId, banner: AdminId) => {
console.log(`${banner} just banned ${banned.toUpperCase()}`)
}
const notifyUser = (banned: UserId, notifier: ModeratorId) => {
console.log(`${notifier} just notified ${banned.toUpperCase()}`) // notice toUpperCase here
}
const banUserAndNotify = (banned: UserId, banner: ModeratorId & AdminId) => {
banUser(banned, banner)
notifyUser(banned, banner)
}
const getUserId = () =>
`${Math.random().toString(16)}` as UserId
const getModeratorId = () =>
// moderators are also users!
// but we didn't need to tell it explicitly here with `as UserId & ModeratorId` (we could have though)
`${Math.random().toString(16)}` as ModeratorId
const getAdminId = () =>
// just like admins are also users
`${Math.random().toString(16)}` as AdminId
const getModeratorAndAdminId = () =>
// this is user is BOTH moderator AND admin (and a regular user, of course)
// note here we did use the `&` type intersection
`${Math.random().toString(16)}` as ModeratorId & AdminId
banUser(getUserId(), getAdminId())
banUserAndNotify(getUserId(), getAdminId()) // this fails
banUserAndNotify(getUserId(), getModeratorId()) // this fails too
banUserAndNotify(getUserId(), getModeratorAndAdminId()) // but this works
banUser(getAdminId(), getAdminId()) // you can even ban admins, because they're also users
console.log(getAdminId().toUpperCase()) // this also works
getAdminId().toUpperCase() satisfies string // because of this
banUser(getUserId(), getAdminId().toUpperCase()) // but this fails (as it should)
getAdminId().toUpperCase() satisfies AdminId // because this also fails
You can also do stuff like:
const superBan = <T extends UserId>(banned: Exclude<T, AdminId>, banner: AdminId) => {
console.log(`${banner} just super-banned ${banned.toUpperCase()}`)
}
superBan(getUserId(), getAdminId()) // this works
superBan(getModeratorId(), getAdminId()) // this works too
superBan(getAdminId(), getAdminId()) // you cannot super-ban admins, even though they're also users!
[0] https://github.com/sindresorhus/type-fest/blob/main/source/opaque.d.ts https://github.com/sindresorhus/type-fest/blob/main/source/o...
[1] https://www.typescriptlang.org/play/?#code/CYUwxgNghgTiAEYD2A7AzgF3hqBzAXPAK4oCWAjkQmgJ4C2ARkhANwCwAUJyAB4AOSGFgw0+CACp4AwqhykUIGAB5xSANYgUAPngBeeAG9OASDhRgqCDXgBtHLgC6hVRpTsOAX3ecRY+JNwVdU14XgxNYDR4AAUYJDEhGgBpEBoAGn88AFkQHGAoHB19AJkUOQVlAxsk+Hl-YJQnTNwcvIKoDy1vDgB6ACo+zgBBDBwwAAt4KHgAInsZ7CQplCmYBlIMGFhrXxAAOn9x0iioCAgkAHcomiQiRcQzcPhgY4x5MGFREDQMjHGC1aaADkWCgaDQpFwKCgDAgCAwS1QCCgKCQf0UGQAZoJ4DASG86AgAG6nKhRP4Ai63CDAeBo8aKC7HBD-InwhnwNBQQnYL4HAAUAGUQAheNy+HC0HsAJScTjiDm4TSKUhgXl+Piwbm5RRRMAo+AMZEoGh-eS4PZyjgAOTR7IBAAMAPKayggB21KJoJCEi7-LBITFTeB0Y6owkweAMuAZOoUjAZEgQUgaWzenmncIwaFvNm1OgSkCEsoFUioNAOfnjUZ8ND4Ho9XAbcZEBh7ZB0HqhsBxb2YjA9cRfQU90h8AfHNBknoAFgAHABOACsAGJJ1QO8WMABaGcAJiXAEYAMwHud76W-DmCSHyU6Jku4JW03ae+Co0EfIinKxR0jAUAUAFABRPYLSxEgPjLdBsH9KYwDAEBx1ghB8TwZ91QQfUVkwUgzkNLCfxAWkmT+FDZniKA3QWNkYAhVB4G3bdDSILAP3gIlVRAbdaK5GUrSGCBvXfO1YIBM0olIAs4S3UsGOOeBk1DcIXyWaYIRQXA4WwPADgASSDG47guFFhDUs5LhDIgIDeQsdNwH5iDQBAHQCZ8PXkTAQHMS0uA4GwACVvNpOhBGRJhWPsjDdilStqwwWt6y7YjSCIOh2x9HoAAEUk4lAACEAHE4GVHopxgTi8twbd0Rqr40FHcdt3AJBaC8uhtwYLYUBeTTtxRYA6rEGr0PNbcADYwExcaQCPcalz3Y9gFlPyFUUFkoDzI0Qmc2jTmeY4wCIcFoJOCLQQA81OSkvDYHgTFvIwIg4HJJYhzEEcYDHDADgAVRQbEhBIAoQCsDIPyOTSozBeB+RoXJpRDJA2VpQGTJgYB8BMZibCyVVe0DAd3pAT7vpXPcAAY9zims6wbJs-lbDLO27An+0HYdGoncFp0pi9sdsPGe1awmOY+rmV0PJcZwpucaYSunG2bJmOx6IW2aJzmvvHHp12+HopZlucVuMHH1ZF9nidJ8dJaXOcKYAdnlxL6eVttVfNvtNfF7XuanfWpfth2Vs4LKxWkkBOAdaPOCkgQhEwwwXSoqgPHuuI6HgEEvm3B7MCBbpXyGBDbjKa00qNSN9GTt0lBQCuMSz4vkHxcvGEUIEuh8L54Gb0uMHy04UUQvR4Brqg64bmAMiBPv8UH6AUEQzvugbQ4XJcTQPU1LZCSzKYLKuciOz4PDFHuF5MQeuAylIEGCIwC4RRWGKMj9daYZmEg1FRC4UAWJk+EPwHAAGI4nDoWDIyB0D-nPuie6zBzhMihpgPEX4XpYw4K+BU5onQKFHuPEAShUHmi7lgnuODNLiCpAQ10E8SGaTIZwNeqhj4+lPnCaeKFnKYROHAA+SDiL3CNIgMEZlQhQAmHSdEkYYbomsKydk1BtTEB6ooKwV1dgHDWtYWACAmBkQdAw3A8AABkhh4AAH1LGUTdNYwg39f4rA8A6XyLC1JEiQP+WCCkjSsjLM9DIRl4BUmsrSTy4RzDwE1OCciMxN7-2iVqPejdkypm9L5V81oQAXEobgPBCBq50KIcYmeeSCkr27n4bJuTIa4GoUsIpKcSmbHNGUupDTKl+TXraC4KEaBAn4SfOE4RfzOR3vfV+nIljwOxIfK6gCID3SgHhe4J8z6+UxJBN4DEeyPRADU8p+D+TSkIIcupBTDAmDXkMa4txRE4RFFAsRV1MQZ2DMYt8uF8I-kuMRA4AAJP5tEgn3LoFAaw7F9SYHgOc3B+CESwpyXkhpGQBocS4hxXUUBfKmFyM9FYQIFB9LNFDJEQIphRDhZpAp7gPBWhYUcKIizlmrMResuEaLyQMl0fwrZPVtQljONYS+19NCfDEFKTg0CYWkvqTQ-QeyQbUvyQoE5lKkW1PNA01ePR4AAvWkCE4ENz4QO0oGOkxT1Tmk2ds6CDx9lzzLlPE5hAnUYDbpXK5HA8VPWzPAPcGr3WesUHSq0-KoIMSVBgLISIaBgJgO6-kkiW7OvbjAN1JdW5T1Ob3LNZQF7DwQEYH1cA-UrBnEG-NA8h5LxAGG7peqcHMrwksjl1AiAIW+GgLZwrfLRtjQoeNggk1KvCMGl10ppS6vgPlSKEkQmoBBBkI0+pjoIA2NDV6BFQj8GTGADYv4YnOVpDDA0DoJ3po9FozgA640JqTReGdABNe52EnIIFsVQDipJviKRTEo3lwIAxWoREgXyMrPypo9VPUeY6QCXsricmdzaQmtsQOws+nJO2IXBL2qwEHyxsRyYh8++gU39xDZGAA1AGmdtyPmwJEuEUFdx33Jp6pycY1JloftxOAZ6EI8wxQQZGIykYv3wnqvccFqZ0SZ1QagJUsjOPcbEPh6w3kIRwJmXyKpCBoi6gYvoEtxh-yEEIZPdNGRDN0VQGQ4w0JCSEGMXS9w0cHRWiyvqcIuBBDWGJqHUAfA4A+aEb9HhBgsrJhQGoZoz404ROCpwPoPRuD8EEBKhAlniYZASaPRxlxtCj2JmY5opRyiKCCK4JhvQBicCCrRUEKxJN0kjPYaKPcGCSLi4ijYUQbxNmhEs18DBrBwFClVciZgLAoF-A6OweAHCuKtAAdXGNYBSC6FC4a5DAGgAB+K0h4-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-R-iK2lAOAdCSe9BQCdExBUC0A9G9AgDZG5WOA703WyZenFSiHydFMKd8ZKdODKdSetGWBfH9FjCDGeeADfh406KQHueRB-XWM-XmIoUqbWkTnEDfAZCLzjHvNMfcfBE8bvm8ebHIgdCBGmaBGvW-NWhSNgBxu0nDN4KiFCjhKgpsG4xJSNLcemC-zUFDLOdmJJyNoaZAF7WMvRNSnSlVjDgpBQBeh6bUB6HMBJDrUGjJN9hGg7MZLQG3DjjiBRm3CBMZO3DhDZAgG3HVe3CXEdj3ExDAEPAPCjcxABepzcrhFLq8sZwTgrvuICr5xrpCsLh7mBdQDBYhd3uAHibaSzhFbyzythxwjlLmJoAhf5A2GcERhLXrMOumb2GMXrabeIo1QLdBfBfEDIXpT8gWqedgGchUGx04PgAHaLYEO0C0F7Zbe9XbaLJWE7eed7cRhhm9IwF9K5agBUGSdxa6VHeN2r3gB4FHm7cxBoH5AMCjFBnOEICBC-xpApQ8GnUvZhRJdHm3Z4GnUQPXifNuJJY8iiCfbZdffRaulHe4dDl4f8zsNp3oOEYy3TZ7n3cPbaBwBUDMIsNkb0YCEMbqsXdd0kekfMNnZkMH0UaUJKy+GcLEBfscBsFI+1AcCCMGA4Ea0HyP0jtGx63uHtMGzvBG17v40mxf3wnXKjI4HW02zhaiB227W2COz8hOxALO1uMoJRuRAGzuwe3gCexew4DezkZmO+3EF+wpn+wr0EEIBmFBwJTA5EaECEXHZWAIpxEFJRxUQxyapnezH2lCnx20iJwYEilJzRB3Qpyp3QJLrpzQO8qw78D8vuLZxQA51T0ruC6Ctrvzjzb8GbxLaUCl1lzbxVzVw1y1x1z1ybjFy+Cvsg1N0bwt2a7EGtxajtwoOiub0o7asUA91MxH0V390D2Dzq7D0KgjyjxjxnQTz+ARP647IzwQXmShlz27XtULwJZLwBL8Ec5gDTprzjXrzjSby65byq99w7y7x7yc7kYH0SGHzu8VzHwOdU7i6n0wBn1CPn0waKaXzTJX1RDX0oM3yWDW4whZQP10+P0rz-wvzKCANHlh8gIf0Ls8qQ8wJQ58PQ486yxAJy850oP0efHYJo8sL0bo8SHkLRRNCEL0AayLCAcEggB0Mp+IgMJK4QA0I5+0IhOLeKBndp6kPp7kKUcUI8LUNM2MA46ZQCGOdM9SAtXEBY5ADY4cCaBZZkckPSpsPPgkebQCDyy1516V+OACHcJUNUPUPZ7ZE5+54MPt8IE0cUBMA8BsCew18t-mKWwcBMGcG6CtAZWCPgD6F7gXoXvgCkCdAABFgJe58onQZcU+9Ju8E-og9JgJE-4AQEAonQsgoxaYkotgLgwI3Z10YAzTxUsSNJgAXpBAWxVKc266egKZMQHZTxjwHZDxppHYHoZwZwQAFxjwZxgBDwHY5wQAGAFwHZzAB-wAwB8g5xe+ypbg6+QAehJM9hxqMBzOo-4BOAT-+R1t3m3x8pxBVt4+pAKYy+FYK+oAq+WLWxa-6+yhG-5Bm-vhW-joPQDvpgC7498++A-Ifg7BH5j8J+U-GfnPwX5L9gAK-BCOv0377pNAzkbcAhApgrQo+QjM-kwWcgwA9ItIGhulToZAhiCJA4AJ3EIGEDXwsaZsgUEECkDyu1A0gTPCYGKAWBNAzuPWQEGCChBwgkDh+ExRPwMMjOE9GhjIgcDaQ0YWnPWQYE9whgwAUMCgDYHkDp6cg-XGoPkCkD+BIgowcYMEFrx0UCg+gRwHrJtdusKAagaPH5C2CFAmMHTooE4GGgUQFQN1HoI0G8ZdAOgNtgIJlTMB9g5wXAPyAdAAASAwE4MUBpxbsUpDwSgGcHwBohsQw-kgF+jN1FAm9ZyCchcR4D4AF7Kwc0RhRiD729g-QI4M8HERCAOg5jHMVICKBCA3ArYAiBoGIx-Ba7IIeWBCF7AwhEQ6IeUKaEwB4hiQ4YUIjSE1CMhWQhILkJAD5CHQiMesmvDEEjwEQswnIWCBZDrRCBxQ6wSblsHUChgPUW0G8HvYOD0hdQ4ge4NiHA5WhvAtgeYlUHqDSBnQgIYQPrJHDiB1Q5IcRBXQ1CYAhQ+suUJoDUDfhzggEX8KBF7CrQBwq9tGjkEOD3hnw24tEKyAFBxgewbqBYDoAnI9gCIQUHKX5AclpQLiDVHILhGlCYGz1SljwPaGaDYYKIkoSsL1Thd6RggPhMiB5ofo6I6NFkSB2i5YAJBLwYAEcwUCTNRkSyTdFDQPQYBfwCgmQZMAdAwwkR5iB4QyKHqvMsI5Lf8ityIBgNgRaIgwBiL+DYiBoPofEYSOJGkjyRMMDUawOABUi2u0aF4foLIFMi9AhhUwXqgSEwpha0FdQVyJNK81iBx-AQVEJNGYjzRPUS0QC2tGFMSR40MkbTV7g+DSBlg+ETCjvTMD2hJw4AG6N8HIivRqIteJ-SiC19r+TocQACjxwcjIwQwa0AX3MDqDYYdjfjBbXgq8jImQYFuHRBABGjVhokRUSKO8R80OQDoUxAZ2OZZhnIkaFAKiMjGmisROIuMQSKQBEjExto1MQ6JoFlZCxGYkoYQO+GKB+QiIm4cABOQZBXR6Yy8VOmPEohjhpwtED23PFuC7x143IAeLvHLCTBIHBdJiBWRCQHxdg4gfmLOGvjcgcgq8fABzH1jSBJyX8cILLEpFAJeEV6EgBAlPjgAEE+9meKgkXiYJcEtoSOh6jfjEJgo+dADWvzhiTxJVG8a8I-GwSvxt4iiX+OMFrxgk76azkkMDGeQV04AXWl7R5SDJuRwkWvuZ0IHBC4Q-QpAOEIYnuirRmQ7ITAHmFsT2JAglCSNR5GatwxCk3wUpM2GqTth6qLkG8B7RNCvQcpDSWvFXRCS6WPiSSSULon4SMA0E9UvpIQnxjlJcwkyVOiMG2SqJCkNCUJDbGSQ+iAxGkIUM8l3j1xRktSYjDMnHBMQlktMYxPYm2TBJ447ScJBClOS14N9d9HfSWAWBRSRAK+P+g0CYIpJJuKcAkEHgrB9A07A3q4JoHLsrh8AYCDwEgBEBQAJ7NKe6Mo53DvBjE94d0KzF9CBhkYu4WMJhR1TFAnUaYakJiHTC4pKkhKQUNhF+R6y80mAA1NcnuTPxGAcif5I0kiCtJzKQQFq0IG7T9pxEx4UxJinqSbJeqBdLpJ-w3SiA9UlEK5JOlHSTpSEs6ayPgBcTPBcXXaYtJWAtj+JoQL7LBFuBgN+kokkMbyLQCo0gAA https://www.typescriptlang.org/play/?#code/CYUwxgNghgTiAEYD2...
- hombre_fatal 2y agoFinally someone writing practical examples instead of Animal / Dog / Cat.
- arctek 2y agoThanks for the examples, I'm working on a TypeScript code base at the moment and this is fantastic way of adding compile-time typing across many of the basic types I'm using!