Skip to content

Instantly share code, notes, and snippets.

@Cretezy
Last active February 23, 2026 23:51
Show Gist options
  • Select an option

  • Save Cretezy/7ac7643a09295621a0f7b5d1cebfa8cf to your computer and use it in GitHub Desktop.

Select an option

Save Cretezy/7ac7643a09295621a0f7b5d1cebfa8cf to your computer and use it in GitHub Desktop.
ID Type in TypeScript
// -- Usage
// Declare ID
export const exampleIdUtils = createId("example")
export type ExampleId = ReturnType<typeof exampleIdUtils["generate"]>
// Generate IDs
const generatedExampleId = exampleIdUtils.generate() // Type: ExampleId
// Turn strings to IDs safely, convert them back to string transparently
const exampleId = exampleIdUtils.parse("example_a1b2c3d4f5") // Type: ExampleId, throws if invalid ID
const exampleIdString: string = exampleIdUtils.parse("example_a1b2c3d4f5") // Type: string
// Strings aren't accepted as the ID type, maintaining type safety
const invalidExampleIdType: ExampleId = "any string" // Type 'string' is not assignable to type 'ExampleId'.
// -- Implementation
export function createId<Prefix extends string>(prefix: Prefix) {
if (!/^[a-z][a-z0-9]*$/.test(prefix)) {
throw new Error(`Invalid id key "${prefix}". Expected lowercase alphanumeric key.`);
}
type Type = string & { readonly __id_brand: Prefix };
return {
key: prefix,
parse: (value: string): Type => {
if (typeof value !== "string" || value.length === 0) {
throw new Error(`Invalid ${prefix} id.`);
}
if (!testPattern(ID_PATTERN, value)) {
throw new Error(`Invalid ${prefix} id format: ${value}`);
}
return value as Type;
},
generate: (): Type => {
return randomId(prefix) as Type
}
} as const
}
const ID_PATTERN = /^[a-z][a-z0-9]*_[0-9a-zA-Z]{10}$/
const ID_LENGTH = 10
const ID_CHARACTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
export function randomId(prefix: string) {
const b = new Uint8Array(Math.ceil(ID_LENGTH * 1.1));
crypto.getRandomValues(b);
let s = `${prefix}_`, p = s.length, i = 0;
while (s.length < ID_LENGTH + p) {
if (i === b.length) {
crypto.getRandomValues(b);
i = 0;
}
const v = b[i++] & 63;
if (v < 62) s += ID_CHARACTERS[v];
}
return s;
}
function testPattern(regex: RegExp, value: string): boolean {
regex.lastIndex = 0;
return regex.test(value);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment