I use strings to represent my data and pass it around. Before using it I have to parse it and get the data out of string. I don't want to check for parse errors on runtime.
How can I give a special type to strings only produced by factory functions and make sure they fail on any other string types.
type MyData = string
let i = 0
function gen_id(): MyData {
return `hello__${i}`
}
function hello(_: MyData) {
console.log(_.split('__')[1])
}
hello(gen_id())
hello('world') // I want this to give compile error
Currently this doesn't produce an compile error.
CodePudding user response:
TypeScript has support for string template literals:
type MyData = `${string}__${number}`
let i = 0
function gen_id(): MyData {
return `hello__${i}`
}
function hello(_: MyData) {
console.log(_.split('__')[1])
}
hello(gen_id())
hello('world') // Argument of type '"world"' is not assignable to parameter of type '`${string}__${number}`'.(2345)
CodePudding user response:
How can I give a special type to strings only produced by factory functions and make sure they fail on any other string types.
You want a string that's a special string, that only comes from a factory function. You are describing type branding.
You make a branded type by intersecting it without something that should only be knowable to the type itself.
A brand may look like:
const idBrand = Symbol('idBrand')
type MyData = string & { [idBrand]: true }
// or: string & { __brand: 'fancystring' }
// or: string & { brand: idBrand }
// point is string & some spicy sprinkle.
// I like symbols as keys since they can't clash with any existing properties
// and you don't want them in autocomplete suggestions.
const brandedStringA: MyData = 'asdf' // error
const brandedStringB: MyData = 'asdf' as MyData // fine
With that you can make a factory like:
let i = 0
function gen_id(): MyData {
return `hello__${i}` as MyData
}
Which works as you expect:
function hello(_: MyData) {
console.log(_.split('__')[1])
}
hello(gen_id()) // fine
hello('world') // error
hello('hello__123') // error
A value of type MyData
can still be used anywhere that a string
can, but you can't pass a plain string
to something that needs a MyData
. It must come from the factory function for that.