If you're searching for something known as branded types, TypeScript determines type compatibility structurally. This means that aliases won't render types incompatible, but we can create structural differences by using an intersection type and a unique symbol:
type Firstname = string & { readonly brand?: unique symbol }
type Surname = string & { readonly brand?: unique symbol }
const firstname: Firstname = "John"; // A string can be assigned because the brand is optional
const surname: Surname = "Smith"
function print(name: Firstname) {
console.log(name)
}
print(surname); // Error - unique symbol declarations are incompatible
There may be different variations of this concept, but the underlying idea remains consistent. You may find similar answers helpful: guid definition, index and position , and others