Hur man skapar en (python-liknande) NewType i TypeScript


I Python (typing) kan man göra ett type alias (vilket inte är något annat än ett nytt namn för en typ) och en NewType, som är en typ som är 100% densamma som den underliggande, men som kommer att behandlas som unik av typesystemet. För att snabbt visa skillnaden:

UserId = int   # Alias, meaning it's not considered a new type
def get_user(uid: UserId):
  # so this is the same as "def get_user(uid: int):"
  ...


get_user(3)  # and this is valid
UserId = typing.NewType("UserId", int)   # NewType, meaning it is considered a new type
def get_user(uid: UserId):
  # so this is NOT the same as "def get_user(uid: int):"
  ...


get_user(3)  # and this throws an error (in type checking)
get_user(UserId(3))  # (this works)
user_id = UserId(3)
get_user(user_id)  # as does this

Observera att även i fallet med NewType är uid-parametern vid körning bara ett heltal, och kan användas på exakt det sättet.

I detta triviala exempel är fördelarna med NewType inte så tydliga, men om du har en funktion med 5 parametrar, några user_ids, några user_age, några department_id, osv, är det en livräddare om typkontrollen kan varna dig om du matar in parametrarna i fel ordning (och ännu bättre, om IDE:n bara föreslår exakt den variabel du behöver för varje parameter, eftersom den vet att du bara har en variabel av typen DepartmentId tillgänglig!)

Hur gör man samma sak i TypeScript?

I TypeScript kan du enkelt skapa typalias:

type UserId = number

men hur man gör något liknande NewType (där TypeScript-kompilatorn kommer att klaga om du tilldelar ett enkelt number till en funktion som förväntar sig ett UserId).

(Observera att för anpassade objekt kan du göra en underklass; den här frågan är mest riktad mot primitiva objekt (sträng, nummer, etc))

Svar #1

Med lite TypeScript-hackande kan du skapa dina egna nya typer:

Definiera din nya typ som baskodstyp plus ett falskt fält:

type UserId = number & {__my_hidden_type: "userId"}
type DepartmentId = number & {__my_hidden_type: "departmentId"}
// etc...

function getUser(uid: UserId) { }

getUserId(3)  // gives error
const notUserId = 3
getUserId(notUserId)  // gives error

getUserId(3 as UserId) // works

const userId = 3 as UserId
getUserId(userId) // works

I praktiken gör detta exakt vad jag skulle hoppas, förutom att IDE:n tror att det finns ett __my_hidden_type-fält på uid-variabeln; fortfarande, med tanke på att det är en hack, skulle jag ändå vara nöjd med en renare lösning.