Its bit sad that startsWith doesn't narrow the type, making this pattern slightly less convenient. The GH issue: https://github.com/microsoft/TypeScript/issues/46958
What is the problem with the workaround suggested in the last comment there?
Adding type safety to object IDs in TypeScript
21–30 of 61 posts
Re: Adding type safety to object IDs in TypeScript
#22This 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 easiest way I know of is declare const isMyID: unique symbol; export type MyID = string & { [isMyID]: true };
Re: Adding type safety to object IDs in TypeScript
#23 type Target = 'currentNode' | (string & {});
const targets: Target[] = [
'currentNode', // you get autocomplete hints for this!
'somethingElse', // no autocomplete here, but it typechecks
];Re: Adding type safety to object IDs in TypeScript
#24Type-prefixed IDs are the way to go. For completeness it's worth noting that the first example using the `string | 'currentNode'` type can be slightly improved in cases where you _do_ want autocomplete for known-good values but are still OK with accepting arbitrary string values: type Target = 'currentNode' | (string & {}); const targets: Target[] = [ 'currentNode', // you get autocomplete hints for this! 'somethingE…
Re: Adding type safety to object IDs in TypeScript
#25Re: Adding type safety to object IDs in TypeScript
#26Type-prefixed IDs are the way to go. For completeness it's worth noting that the first example using the `string | 'currentNode'` type can be slightly improved in cases where you _do_ want autocomplete for known-good values but are still OK with accepting arbitrary string values: type Target = 'currentNode' | (string & {}); const targets: Target[] = [ 'currentNode', // you get autocomplete hints for this! 'somethingE…
Is (string & {}) semantically intuitive or is it kind of a hack? I don’t understand what it’s supposed to mean.
When converting something to this type, it will fail unless you cast it, but it's a compile-time cast. At runtime, there's no conversion.
This is essentially "lying" to the type checker in order to extend it.
Re: Adding type safety to object IDs in TypeScript
#27Re: Adding type safety to object IDs in TypeScript
#28Its bit sad that startsWith doesn't narrow the type, making this pattern slightly less convenient. The GH issue: https://github.com/microsoft/TypeScript/issues/46958
Re: Adding type safety to object IDs in TypeScript
#29Its bit sad that startsWith doesn't narrow the type, making this pattern slightly less convenient. The GH issue: https://github.com/microsoft/TypeScript/issues/46958
What is the problem with the workaround suggested in the last comment there?
For example:
type UserId = `user_${string}`;
type GroupId = `group_${string}`;
const addUserId = (id: UserId) => {
// do something
}
const processId = (id: string) => {
if (id.startsWith('user_') {
// type error here:
addUserId(id);
} else if (otherCondition)
// do other things
}
}
Instead you define a function: const isUserId = (some: string): some is UserId => some.startsWith('user_');
Now you can use it as follows: const processId = (id: string) => {
if (isUserId(id)) {
// no more error:
addUserId(id);
} else if (otherCondition)
// do other things
}
}
[1]: https://www.typescriptlang.org/docs/handbook/2/narrowing.htm...Re: Adding type safety to object IDs in TypeScript
#30I considered doing it on a recent project, but it doesn't seem very common so I was reluctant to introduce it.