TIL: String type with a specific length in TypeScript
We’ll declare a Phantom Type:
type StringN<N> = string & {
length: N;
readonly X: unique symbol;
}
const isStringN = <N extends number>(length: N, s: string):
s is StringN<N> => s.length == length;
export const stringN = <N extends number>(length: N, s: string): StringN<N> => {
if (!isStringN(length, s)) throw new Error(`input must have length ${length}`);
return s;
}
Then we can use it as below:
export type MicroUuid = StringN<4>;
export const microUuid = () => stringN(4, newUuid().slice(0, 4)) as MicroUuid;
let s4: MicroUuid = microUuid();
let s5: StringN<5> = stringN<5>(5, 'hello');
s4 = 'any string'; // invalid
s4 = s5; // invalid
We can extend it further to make a specific type for our MicroUuid:
type MicroUuid = string & {
readonly X: unique symbol;
}
const isMicroUuid = (s: string): s is MicroUuid => /[a-f0-9]{4}/.test(s);
const asMicroUuid = (s: string) => {
if (!isMicroUuid(s)) throw new Error('invalid input');
return s;
}
const newMicroUuid = () => newUuid().slice(0, 4) as MicroUuid;
We can use the same declaration readonly X: unique symbol
in multiple type, and they are still different:
let s5 = stringN<5>(5, 'hello');
let id: MicroUuid = s5; // invalid
And another trick: Flavoring: Flexible Nominal Typing for TypeScript.
Let's stay connected!
If you like the post, subscribe to my newsletter to get latest updates:
Author
I'm Oliver Nguyen. A software maker working mostly in Go and JavaScript. I enjoy learning and seeing a better version of myself each day. Occasionally spin off new open source projects. Share knowledge and thoughts during my journey. Connect with me on , , , , or subscribe to my posts.