# Dynamic optional and mandatory fields with advanced Typescript generics


# Introduction
In order to have the correct `undefined` status for fields on the built objects, we need to be able to have the type of builder that evolves as methods are called upon him. The idea here will be to have the return type of any of the methods of the builder to be a builder type of a new-entity that is the initial entity with the additional field added to it.

# Advanced generics at the rescue...

To do that we will need a parametric type that from one initial type extends it with a mandatory property. This could be done through this type:
```typescript
type TypeWithAdditionalMandatoryProps<T, P extends keyof T> = T & {
  [K in NonNullable<keyof Pick<T, P>>]: NonNullable<Pick<T, P>[K]>
}

```

Some explanations about this type :
- `<T, P extends keyof T>` : Tels the type has two parameters. Thanks to that `P extends keyof T` , it tells that the second parameters should be the name of one or more of the properties of the first parameter.

```typescript
type NT<T, P extends keyof T> = {}
type OK = NT<{ x:string, y: number }, 'x'> // OK
type Erroneous = NT<{ x:string, y: number }, 'z'>  // Type '"z"' does not satisfy the constraint '"x" | "y"'.
```

- The `T & ...`  tells that it will extend the initial first type with another type
- `Pick<T, P>`  : From an object type `T` creates a new one, only constituted of the properties provided in `P` : 

```typescript
type PickedXY = Pick<{ x: string, y: number, z: boolean }, 'x' | 'y'> // { x: string, y:number }
```

- `NonNullable` : Removes `null` and `undefined` types from a list of union types.

```typescript
type NonNullableTypes = NonNullable< string | null | undefined | boolean > // string | boolean
```

As a whole we have a type `TypeWithAdditionalMandatoryProps` that from an input type `T` and a list of properties `P` of `T` build a new type `T-Bis` with all the properties `P` set as mandatory properties.

# ... with type recursion to spice things a bit

We can then enhance the `BuilderWithTypeMethods`  as follow :

```typescript
type BuilderWithTypeMethods<T> = EntityBuilder<T> & {
  [K in NonNullable<keyof T>]: (value: NonNullable<T[K]>) => BuilderWithTypeMethods<TypeWithAdditionalMandatoryProps<T, K>>
}
```

Here also some explanation might be needed
- The type `BuilderWithTypeMethods` calls `BuilderWithTypeMethods` making it a recursive type. The recursion of type was not supported in the first version of typescript, and this now allow some advance type construction.
- The addition of the `NonNullable` key word allow to get rid of potentially `undefined` methods when building optional properties from the input type.

The type now says that each methods of the builder returns a builder with a type being built is the same type but with an additional property.

Here a sample of usage : 

```typescript
const cheddarBuilderWithKInd = EntityBuilder.getAn(Ingredient)
  .kind(EKind.Cheese) // BuilderWithTypeMethods<TypeWithAdditionalMandatoryProps<Partial<Ingredient>, "kind">>
const cheddarBuilderWithKIndAndWeight = cheddarBuilderWithKInd
  .weightInGrams(170) // BuilderWithTypeMethods<TypeWithAdditionalMandatoryProps<TypeWithAdditionalMandatoryProps<Partial<Ingredient>, "kind">, "weightInGrams">>
const cheddar = cheddarBuilderWithKIndAndWeight
  .build() // TypeWithAdditionalMandatoryProps<TypeWithAdditionalMandatoryProps<Partial<Ingredient>, "kind">, "weightInGrams">
```

# Dynamic property names

Let's add a last modification to rename the name of the method so that it is more builder like :

```typescript
type BuilderWithTypeMethods<T> = EntityBuilder<T> & {
  [K in NonNullable<keyof T> as `with${Capitalize<string & K>}`] :
  (value: NonNullable<T[K]>) => BuilderWithTypeMethods<TypeWithAdditionalMandatoryProps<T, K>>
}
```

- The keyword `as` allow to rename the `K` property 
- The `as` keyword can use template string to construct complexe transformation of the `K` name. In our case we use the built-in `Capitalize` type to construct a new string type with name being capitalized.

```typescript
type Capitals = Capitalize<'abc' | 'def'> // 'Abc' | 'Def'
```

# Conclusion
Thanks to the usage of typescript generics, few `typescript` built-in types and keys words like `extends` or `NonNullable`, we were able to build a type that evolves as we use it.

We can then use the our generic builder as follow :
![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1650399296127/kZPovLoRx.png) 
It's magic ! 🎉

So now we have a class, that a type level works as expected (build time), but our class need more polish to be used at running. Thanks to javascript `proxies`, we will be able to dynamically change the behavior of our builder object. This is what we will do in our next and last post of this typescript series.

If you are already familiar with advanced typescript features and just want to see what could be a convenient-to-use builder infrastructure that makes good use of those features, please have a look at this repo : [berlingo-ts](https://gitlab.com/ArthurCrl/berlingo-ts)
