I'm working on a User class, but I'm stuck on how to handle the user ID since it's generated by the database. When I try to use the `createUser(user: User)` function, I run into a problem: if the ID is a `private readonly` field, I can't create a User instance until it gets an ID from the database. Here are the options I've thought about: 1) Making the `id?` field optional, but then I'd have to check if it's defined everywhere, which could get messy. 2) Using a union type for the ID that combines `number` and `undefined`, creating pseudo-constructors like `static create` (for creating users before they have IDs) and `static fromDatabase` (for when users are fetched from the database to assign IDs). This feels weak because a critical field like ID can't be undefined. 3) Creating a UserDTO class that doesn't strictly require an ID, which would double my entity files. I'm looking for some advice on this; what's the best approach to take here?
7 Answers
Just grab the username or email (or both) and the password, make sure to hash the password, then create a database entry. Your DB should generate the ID automatically, so you'll get that back along with the other user details.
It's better not to make the ID optional in your User class. You could use TypeScript's Partial type for cases where the user hasn't been fully created yet, but those instances should be rare.
When you create your user table, set the ID field to auto-increment. That way, you won't have to manage the ID when adding new entries; the database will handle that for you.
If `createUser` is the first step for a new visitor, maybe you don’t even need to pass a user object? Think about having `createUser()` simply generate a user and throw an error if the DB operation fails.
You could also use `type NotAUserYet = Omit;` and do something like `createUser(user: Omit)` to clean up your code.
Have you thought about generating the ID within the `createUser` function using something like uuid, nanoId, or snowflake IDs? It could simplify your process.
It sounds like you’re caught in a loop where you need a user object to create a user but can’t create one without the ID. Instead of using the same interface for both the creation and final user object, consider separating them. For example, you could define a `CreateUserPayload` with just the necessary fields like name and password, then use that for your `createUser` function, which would return a fully formed User with an ID afterward.
You could also use a variation where you specify just certain fields like:
```typescript
function createUser(user: Omit
```
or pick specific attributes like this:
```typescript
function createUser(user: Pick
```
But then what should I pass to `createUser(user: ???)` if it's not a complete user?