Reputation: 3341
In Python the de-facto ways of defining "data classes" are either by using the built-in dataclass module, or (for more power), the pydantic library.
Both of these solve the same thing: giving the ability to define and use data-centric classes (classes with little or no functionality, only meant to store and validate data).
An example of pydantic would be:
class User(BaseModel):
id: int # required
name = 'John Doe' # optional with default
friends: List[int] = [] # optional, defaults to empty array
I could expand this by adding validator functions to check for minimum field length etc.
I'm looking for something similar with TypeScript. This is what I've learned so far:
interfaces would be the obvious choice, but they only let you define the data structure , you can't use interfaces to set defaults
I've looked into zod but it looks like it's more for validating "unknown" data. I'm not able to use the infer
functionality to generate a type that correctly returns default data
I know there are also tricks like create a separate function for implementing "overrides":
export type User = {
id: number;
name?: string;
};
export function generateUser(
props: User
): User {
const defaults = {
name: 'peter',
};
return {
...defaults,
...props
};
}
But this seems a little convoluted to me since I have to define the "default fields" twice (both in the type and in the function).
It also has the drawback of telling typescript that name
is optional and might not exist. I want to always exist, but filled with a default value if nothing else was specified.
Does TypeScript offer a more elegant way of defining defaults?
Upvotes: 0
Views: 134
Reputation: 6631
I'm not sure if this answers your question, but in TypeScript, you can use some helper generics to modify any given type. For example, you are looking for a concrete type where name
is always defined, but not required in the function input.
I would define the types like so:
generateUser
functionUser
type in other parts of your application, you can obtain the type by using ReturnType<typeof generateUser>
.type UserInput = {
id: number;
name?: string;
};
export function generateUser (
input: UserInput
) {
return {
name: 'peter',
...input
};
}
type User = ReturnType<typeof generateUser>;
Another solution would be to do it the other way around. We define the User
type as it would come out of the generateUser
function, but modify it for the input
arguments.
type User = {
id: number;
name: string;
};
type UserInput = Partial<Pick<User, 'name'>> & Omit<User, 'name'>;
export function generateUser (
input: UserInput,
): User {
return {
name: 'peter',
...input
};
}
Upvotes: 1