Trondh
Trondh

Reputation: 3341

Defining a config model with some defaults

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:

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

Answers (1)

Chris
Chris

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:

  1. Define a separate type for the input where properties may be optional
  2. Let typescript determine the output type of the generateUser function
  3. If you want to use the User 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

Related Questions