Typescript - Type System
Tags: typescript, react, functional, programming
Published: 2023-07-03 12:56:10

Hi guys, 

I have always wanted to produce content about programming and technology from some of the subjects I know, so that more people can learn. 

So I am getting courage and starting to produce content. 

Besides sharing knowledge I also intend to learn a lot from these articles. As I am not a genius and do not know everything, so please correct me, give suggestions, theories and let's make this space a environment for discussion and exchange of experiences and knowledge. 

I rolled a lot in the introduction, so let to that is important.
## You know typescript? 
### Do you know how to extract the maximum of typescript?

In this article we will create a **type system** for the a **query builder** to study and explore the maximum of the typescript. So will focus on the type and not in the implementation. [Final code](https://www.typescriptlang.org/play?#code/MYewdgzgLgBAtgTwCICMCSkoEMzAKYwC8MA3gL4xYQxJbYBCVeAKntANwBQnomlRMAIoBXPACcE9YQEsANgBNxAOnl0sKJgApEqDNBz4AlJxgwlAMzEg4mgETCI428dNLHsvMCiaA2renytgA0MLbAYnh0ePIA+nTBoXhwWHK2ALouZgDuABbieHZJKbICAFxFqSEkMBWypYnJcgACtUqgcLYwZJlKKDIKmsac0mBQ4uZY+DAAqo5ipCYwAfVgwnAo4lymYFhwePXQYiMA5ls1jXUwhydnAA5UEFkgYvIHUEdgp4vCt6pjsXR6rQxmdwpF-nEoEColwyNwRmMxBMpgAFKzyYReBamZYwVbrTaLHZ7N4fL6mRQQcLSW5QaTgUk3Ra3I74FZrDZiUERKIAqE0GGcOHDUbjSYEADyL3E2KWrzxHMJpgc4hiuPxnLu6MxUDV8o1SpgAEdhDg6VAEOyCVzFmDeZDoSChdwLbcCMCsIxHKxoAISN85vVZoaWSAMV56miwzqzs9FGJ6lL47DuIpgLIsBE8bs2PcpiJxJJ+vHZeZhLg6eAYH91EwADxIegAPk08hQQPohnqBYkUjk8YAsngoDkwxAG82U5xXQQe0X++IhyOx3XFo2gotmOoPDUAB5jMDyagAazwCBA5ho9AEp-Pl-XiwAwiBZGtIHuD0eYLeL1efFuUA8NIbzPX9G3-bc8DSDdTGfV84EgABlPAPC8aIPzwQ9qAAQTEMQsAQOs4LfCAmwEXD8MI4iENIjcyOIf1TEsax6jrZgMKwmAAI8FtsEA-YuMMIgyLnPsFEXYdRyPCcQmYJsYKuFDPH5OtEI4r8KIIoiXxI+iYE0qidJopsWwgepEKEwgRNEXtiwk5dpPXLjIJCajIBCRD5MWXJ8lYxZTAAdXU6gBywU8AryCJEPeE5tPg98ADIrhiz4vNMUwADVgpgABRfd8K8Nz6AQCL8jrAKyMWFt-JgLJ6gChTTAAN3qaofAAaSWMAYAytIOwg-i0h8LKkrciBgLhUxLOswsxMHSSVyc7i8Fcoz3JgMbkNQ-40pgPp+3qQZhJgKM4GkRw6xRaRgGPCcBqA1b4ogLblOiHwDTENITOdacEDdGBQvCyK8GislV1gtbsuuT4QkWCU3XwqBnihlLjgEZ84HuI4IHAeHxDoZ4IFhzKsFfAg8H3TCv2htHiBpzg9IAAwAEhINyKFZvHEeeChSlZjLSdEMhGa4X7-oxrHztxhGCbEahiFsQhOgAH1COsmxV0ImyVmBVdsOsldFmdcvyyYoCKkrgdU949NB7LAbwUqotRusRnMGVHybRYAH4NsWFY8CazYgA)
#### What is the query builder?
As this is not the proposal, I asked for the GPT
"A query builder is a software tool or library that allows developers to construct database queries in a more intuitive and efficient way. Instead of writing raw SQL statements, which can be complex and error-prone, a query builder provides a higher-level abstraction that enables users to build queries using a more human-readable and structured syntax."

We will build something like this
```ts
const user = await QueryBuilder.database(myDbInstance)
    .from("user")
    .select(["id", "email", "name"])
    .where("id = :id", { id })
    .build();
```
- [TypeOrm](https://orkhan.gitbook.io/typeorm/docs/select-query-builder#:~:text=QueryBuilder%20is%20one%20of%20the%20most%20powerful%20features%20of%20TypeORM%20%2D%20it%20allows%20you%20to%20build%20SQL%20queries%20using%20elegant%20and%20convenient%20syntax%2C%20execute%20them%20and%20get%20automatically%20transformed%20entities.)
- [KnexJs](https://knexjs.org/guide/query-builder.html#knex-query-builder)
- [kysely](https://kysely.dev/)

We will learn and have a little notion about 
- `generics` 
- `keyof` operator
- `infer` operator and `conditional` 
- manipulate strings
- `mapped` and `Indexed Access` types

### #1 Mapping the database and table of query
In this step we will use some of `generics`.

First we have to type the function which will server as builder, how this function use the design pattern [**builder**](https://refactoring.guru/design-patterns/builder), do not forget to return the own function to  make this composition `.from(...).select(...).where(...)`
> Using this database shape for tests
```ts
interface User {
  id: number;
  name: string;
  email: string;
  password: string;
  updated_at: Date;
}

interface Product {
  id: number;
  name: string;
  description: string;
  price: number;
}

interface Order {
  id: number;
  user_id: number;
  product_id: number;
  quantity: number;
  created_at: Date;
}
// Use  this DB for tests
type DataBaseTest = {
  user: User;
  product: Product;
  order: Order;
}
```
> Init of query builder
```ts
declare namespace QueryBuilder {
  function database(db: DataBaseTest): QueryBuilderMethods;
}

type QueryBuilderMethods = {
  from: (table: keyof DataBaseTest) => QueryBuilderMethods,
  build: () => Promise<void>
}
```
[Ts playground](https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgKoGdrIN4ChnLAAmAXMiAK4C2ARtANz7lxURnphSgDmjBEVOMAA27Tjz7IADnHToA7gHsopZBy4heTClKJxIRAPr6yAEX0RGAX1y5QkWIhQAFKIqIUEYHE2JlKtAxMICxsauKakkQQ6AhcUmDAiiBiGloEUlxI-tR0UNa29tDwSMgA8ipYeAR+5LlBBBSYUIa1AXmSme6eYK2q7Q3IAI4UcODAYACeOYH5THEQFkYmyOaQBbgA9JtomARgABbA6KsAQsgwysiQHOi4U1Ioa3CnshAAKjHeALw+jc1kDCDLoeLxkVzdLySZTRKBkCqwjbRBDCOBQFAhVjoGSlACKFGgk1OFBEsL+FwoIC8SRAyD0YDgNDeAAoiDQzPoXm9PhwAJRkfGE4mk6AAWQgh3c6A2DxQgqgRJJwlh4slRBOv2qFzcVDIzIZNGEYQA1hBJooYKtOa9MDywLzkN8AHzIeWKkVQVUHKUAGiYNCVqmZDudyAhVGOEAAPAA3RTEJ24Gy4BDJDjIKiTUw0ACSIA4Y1KmqsyFkVoZNo+X0YuDdwuV0AAdPTGSzM9m8wWqRBeUxtsgAERNaAD5AAH0HIJ6o4nA5hI6YjZgOuZc8qUAHvYIjYDpODQA)

#### keyof
The `keyof` takes all the keys of the object and returns the union them.
Then, in this case all **keys** of DataBaseTest, that is
```ts
// "user" | "product" | "order"
type Tables = keyof DataBaseTest;
```

#### Dynamic?
But that way our queryBuilder is not dynamic. If we want to use it in another **database**?

So add a `generic` to get the database that was selected via params of `database()` methods.

```ts
declare namespace QueryBuilder {
  function database<DB>(db: DB): QueryBuilderMethods<DB>;
}

type QueryBuilderMethods<DB> = {
  from: (table: keyof DB) => QueryBuilderMethods<DB>,
  build: () => Promise<void>
}
```
[Ts playground](https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgKoGdrIN4ChnLAAmAXMiAK4C2ARtANz7lxURnphSgDmjBEVOMAA27Tjz7IADnHToA7gHsopZBy4heTClKJxIRAPr6yAEX0RGAX1y5QkWIhQAFKIqIUEYHE2JlKtAxMICxsauKakkQQ6AhcUmDAiiBiGloEUlxI-tR0UNa29tDwSMgA8ipYeAR+5LlBBBSYUIa1AXmSme6eYK2q7Q3IAI4UcODAYACeOYH5THEQFkYmyOaQBXbgxU7IznCTrOA+Nf31cxn7h72sYAAW7mQARAtEE4-IAD7Ij9E0b5ILJbGMBmCwbIqOUoAYTgUGOhFOs06lwg4EMN3uqmeUAgrzA7y+Pwgf3xAJxQJWa0suBsuAA9HS0JgCHdgOhVgAhZAwZTISAcdC4KZSFBrOAc2QQAAqMW8AF54U1oGQMIMuh4vGRXN0vJJlNEoGQKgaNsKUABBECKO7QMU0SXIBXVZBKw1MtUo8Baz1gDbRBDCWEoEKsdAyUoARQo0EmHIoIgN8JgFBAXiSIGQejAcHtmAAPKYOQA+AAURBoZg5AEoyFGY3GE9AALIQO7udAF4umyYi5B1qCx+PCA0tttEDuFouOpNuKhkEvZmjCMIAawgk0UME5VcdU-7g8bUFHmInxYANEwaEPVCWd3Kp9qqGyIHmAG6KYhFmm2BDJDjIKhJlMGgAEkQA4MZSidKxkFkVZ9HFSUZQ4RhcH3Bth2gAA6LMc0lEtAOAsCINTCAqyYBlvldAlvnVHoaMefVoEeJgsJgWcS0YyooEeciCCwq8E1vH8-28MZrVuW1QPA7NSOnbAYLgy0JNtBDc2pNDowHDCDRwtT8PEm0oCImTILIijGUeajPlon0WP49jFCoTiZAOVF8T45ABOvW8gA)

The `generic` will capture the type that was passed in `database()` method and pass to `QueryBuilderMethods` for him use as he sees fit.

With this change the build became dynamic, it can receive any database 
```ts
const myDbInstance = {} as DataBaseTest;

QueryBuilder.database(myDbInstance)
  // "user" | "product" | "order"
  .from("order")
  .build()

const anotherDbInstance = {} as AnotherDatabase;

QueryBuilder.database(anotherDbInstance)
  // "user" | "payment"
  .from("payment")
  .build()
```

### #2 Mapping the all columns of table selected
In this step we'll add the `.select()` method, that can receive all columns of table, in other words, receive all keys from the table.  

How the `.select()` depends on what was passed on `.from()`, we have to add one more generic, the `Table` in question. 
```ts
type QueryBuilderMethods<
  DB,
  Table extends keyof DB = keyof DB
> = {
  from: (table: Table) => QueryBuilderMethods<DB>,
  select: (s: Array<keyof Table>) => QueryBuilderMethods<DB>,
  build: () => Promise<void>
}
```
The `extends` in generic, works like typing for him. The `Table` can receive the keys of `DB` and added a default value that is `keyof DB`
But if you test, [ts playground](https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgKoGdrIN4ChnLAAmAXMiAK4C2ARtANz7lxURnphSgDmjBEVOMAA27Tjz7IADnHToA7gHsopZBy4heTClKJxIRAPr6yAEX0RJCKBAtGTyc5EYBfXLlCRYiFAAUoikQUCGA4TMRklLQMTCAsbGrimpJEEOjWwFJgwIogYhpaBFJcSJHUdFBWNnbGYGYWru6e0PBIyADyKlh4BBHk5TEEFJhQhn1RFZLFgcFgY6oTg8gAjhRw4MBgAJ5l0ZVM1rYGtfXOuG64APSXaJgEYAAWwOiOAELIMMrIkBzouNtSFBOOCvWQQAAqaVCAF4wkMRmQMEtpkEQmR-DMQpJlKkoGROrjGrhUghhHAbMxWOgZG0AIoUaBbV4UES4uEfCggEI5EDIPRgOA0MEAHlMrwAfAAKIg0MyvACUZHpjOZrOgAFkII9AuhRRKiQCUMqoEyWcJcZrtURdUwxQAaJjgwXCFAQAAekBA1uQAGsIFtFDA3shYX6A0GxbhxSH2TAAlQyJKBTQXWQnSmIPKQ9Hjaa1VBLQ8dXrxQ6CJgXWjkJL0GQAIJQKBwLbCsOB5Dpl3irPQnMMk2q80arVF60lsvIGhm1SSnvRjFUZ4QYUAN0UxHF53cCFyHGQVC2phoAEkQBx1m1YdgXMhZI59CCwZCOIxcLnB7iAHT8wVgyUHo9T3PLlMyYT840UKhJQAIhxaBoPlJhrmQAB1cEADFMIAQiwsCKwgEJJQAbWg6CAF1EIIT8p1ZWdcCAA), it won't work yet, why?

That's because we made it too generic. Then we have add more specificity in some points.
```ts
type QueryBuilderMethods<
  DB,
  Table extends keyof DB = keyof DB
> = {
  // Here, needs specify the table, not any `key` of `DB`
  from: <T extends Table>(table: T) => QueryBuilderMethods<DB, T>,
  select: (s: Array<keyof DB[Table]>) => QueryBuilderMethods<DB, Table>,
  // Don't forget to always pass the generics to 
  // `QueryBuilderMethods`, in all methods, it will serve for 
  // the next method that is used in the builder
  build: () => Promise<void>
}
```
We already have that [ts playground](https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgKoGdrIN4ChnLAAmAXMiAK4C2ARtANz7lxURnphSgDmjBEVOMAA27Tjz7IADnHToA7gHsopZBy4heTClKJxIRAPr6yAEX0RJCKBAtGTyc5EYBfXLlCRYiFAAUoikQUCGA4TMRklLQMTCAsbGrimpJEEOjWwFJgwIogYhpaBFJcSJHUdFBWNnbGYGYWru6e0PBIyADyKlh4BBHk5TEEFJhQhn1RFZLFgcFgY6oTg8gAjhRw4MBgAJ5l0ZVM1rYGtfXOuG64APSXaJgEYAAWwOiOAELIMMrIkBzouNtSFBOOCvWQQAAqaVCAF4wkMRmQMEtpkEQmR-DMQpJlKkoGROrjGrhUghhHAbMxWOgZG0AIoUaBbV4UES4uEfCggEI5EDIPRgOA0MEAHlMrwAfAAKIg0MyvACUZHpjOZrOgAFkII9AuhRRKiQCUMqoEyWcJcZrtURdUwxQAaJjgwXCFAQAAekBA1uQAGsIFtFDA3shYX6A0GxbhxSH2TAAlQyMLwch3Z7vU6aC6pQLMwlwfKQ9Hjaa1VBLQ8dXq7chweKHQRro5cgByUKfKDcLXfRTIODCeRwLYvGRyb4PFCdkDQYAIF5gHtMRsAA2LqvNGq1FetS+roF7wmEyFYVvQu9C8hEh5GADcUO3kIubo8UFOPUfN4Ex-pCC9hhAiIQvLPsgNBmriTCYC6aLIJK6BkAAglAUCDsKYaBm8ADaGYugAuuKBbQkWDImmuFoftaVY1s6EB1kwoGsmQkoEdGGJUM8EDCteijEOK5zuAguQcEeWymDQACSIAcOsbSwtgLi9i8wKgpgkIcIwuCrmB0AAHT8oKYKSlQIniZJApchA8pMNpcaKFQkoAEQ4tA9mWQ2NztA8DzIAAmrYDxWZBEAhJKGH2cQ9nVvZhw1PoEXIPZqzrNk2z2ThrnINp9HmkxuBAA)

### #3 `.where()` method
> "_WHERE user.id = 1_"

The idea of this method is receive a string with condition, and a object with the values that will be used in the condition.

The string should follow this pattern `"column operator :value" 
`

Let's create a utility to make the where string and add new `.where()` method
```ts
type QueryBuilderMethods<
  DB,
  Table extends keyof DB = keyof DB,
  Columns extends keyof DB[Table] = keyof DB[Table]
> = {
  // ...,
  where: (
    w: MakeWhereString<Columns & string>,
  ) => QueryBuilderMethods<DB, Table>,
  // ...
}

type MakeWhereString<
  Column extends string, 
  Operator extends string = ComparisonOperators, 
  Value extends string = string
> = `${Column} ${Operator} :${Value}`;

type ComparisonOperators = "=" | "<>" | ">=" | "<=";
```
> Beyond utility, i moved `keyof DB[Table]` to `Columns` generic for more readability.

[Ts playground](https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgKoGdrIN4ChnLAAmAXMiAK4C2ARtANz7lxURnphSgDmjBEVOMAA27Tjz7IADnHToA7gHsopZBy4heTClKJxIRAPr6yAEX0RJCKBAtGTyc5EYBfXLlCRYiFAAUoikQUCGA4TMRklLQMTCAsbGrimpJEEOjWwFJgwIogYhpaBFJcSJHUdFBWNnbGYGYWru6e0PBIyADyKlh4BBHk5TEEFJhQhn1RFZLFgcFgY6oTg8gAjhRw4MBgAJ5l0ZVM1rYGtfXOuG64APSXaJgEYAAWwOiOAELIMMrIkBzouNtSFBOOCvWQQAAqaVCAF4wkMRmQMEtpkEQmR-DMQpJlKkoGROrjGrhUghhHAbMxWOgZG0AIoUaBbV4UES4uEfCggEI5EDIPRgOA0MEAHlMrwAfAAKIg0MyvACUZHpjOZrOgAFkII9AuhRRKiQCUMqoEyWcJcZrtURdUwxQAaJjgwXCFAQAAekBA1uQAGsIFtFDA3shYX6A0H7UwAMKKYTUEAvd2e71hwNvADaTpoLoAuiHff602LM86IDmHeL8z0PgEqGRheDkEmIF6XlmXVKBdmEuD5SHK8bTWqoJaHjq9XbkODxQ6CJgXWjkJL0GQAIJQKBwLbCmNxqgJ8V96EDhkm1XmjVasfWidT0szpjyB7QBKSpgEeRkdVwP0AdWfNgAMpJNwO6xvGLwAGSJAUD4EEeJ4qmaFpXuO9p3t2cHIDQyFkJKCHIBiVDPBAwoAG6KMQ4rnO4hrIN+f4ARAwEFMK0bgfuTYei23rqDwk5MO0gKbmAXzNq2ME8PmMZUDIXDoLkQnQPoyjoAJBAAGpwHGrrcRJfGaPmBncLglawgABgAJNgu7xi4yDWUpInKPZJDWVpOkuOZjD-FsgLIDJcnPIpwkqVALywgARNCkXIAAPsgkXCuKsUJZF4oxfFiXCjFPkILkHDIFQWymDQACSCYClyKCwtg9myI4+ggmCkIcD5g7nriAB0-KCmCkrFaVFUcOsSDykwXUwLWkqRcM0CReNBBdfOEAhJK6aRcQkWTpFhw1Po22JQIQjCJFOaLcgXVPi+M3EPmJBbRdXU4ay+G4EAA)

The `where` can only accept one string that has `"column_of_table_selected ComparisonOperators :value"`

```ts
QueryBuilder.database(myDbInstance)
  .from("order")
  .select(["id", "created_at", "quantity"])
  // ERROR: Only receive columns of order table
  .where("field = :id")
  .build()

QueryBuilder.database(myDbInstance)
  .from("order")
  .select(["id", "created_at", "quantity"])
  // ERROR: Only receive one of operators
  .where("id === :id")
  .build()

QueryBuilder.database(myDbInstance)
  .from("order")
  .select(["id", "created_at", "quantity"])
  // ERROR: Must have the ":"
  .where("id = id")
  .build()
```
See the errors [Ts playground](https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgKoGdrIN4ChnLAAmAXMiAK4C2ARtANz7lxURnphSgDmjBEVOMAA27Tjz7IADnHToA7gHsopZBy4heTClKJxIRAPr6yAEX0RJCKBAtGTyc5EYBfXLlCRYiFAAUoikQUCGA4TMRklLQMTCAsbGrimpJEEOjWwFJgwIogYhpaBFJcSJHUdFBWNnbGYGYWru6e0PBIyADyKlh4BBHk5TEEFJhQhn1RFZLFgcFgY6oTg8gAjhRw4MBgAJ5l0ZVM1rYGtfXOuG64APSXaJgEYAAWwOiOAELIMMrIkBzouNtSFBOOCvWQQAAqaVCAF4wkMRmQMEtpkEQmR-DMQpJlKkoGROrjGrhUghhHAbMxWOgZG0AIoUaBbV4UES4uEfCggEI5EDIPRgOA0MEAHlMrwAfAAKIg0MyvACUZHpjOZrOgAFkII9AuhRRKiQCUMqoEyWcJcZrtURdUwxQAaJjgwXCFAQAAekBA1uQAGsIFtFDA3shYX6A0H7UwAMKKYTUEAvd2e71hwNvADaTpoLoAuiHff602LM86IDmHeL8z0PgEqGRheDkEmIF6XlmXVKBdmEuD5SHK8bTWqoJaHjq9XbkODxQ6CJgXWjkJL0GQAIJQKBwLbCmNxqgJ8V96EDhkm1XmjVasfWidT0szpjyB7QBKSpgEeRkdVwP0AdWfNgAMpJNwO6xvGLwAGSJAUD4EEeJ4qmaFpXuO9p3t2cHIDQyFkJKCHIBiVDPBAwoAG6KMQ4rnO4hrIN+f4ARAwEFMK0bgfuTYei23rqDwk5MO0gKbmAXzNq2ME8PmMZUDIXDoLkQnQPoyjoAJBAAGpwHGrrcRJfGaPmBncLglawgABgAJNgu7xi4yDWUpInKPZJDWVpOkuOZjD-FsgLIDJcnPIpwkqVALywgARNCkXIAAPsgkXCuKsUJZF4oxfFiXCjFPkILkHDIFQWymDQACSCYClyKCwtg9myI4+ggmCkIcD5g7nriAB0-KCmCkrFaVFUcOsSDykwXUwLWkqRcM0CReNBBdfOEAhJK6aRcQkWTpFhw1Po22JQIQjCJFOaLcgXVPi+M3EPmJBbRdXU4ay+HuB1yHQD1TVCpgA0leVlWjRAT1TYoVAzTi81PSta0bVtO17ccB07as6zZNsZ0XdcHQgMIWzIDYSDAGRKD5XuCbIGmUNQN8pYTddNgzTAwAQOa92PRNL3mm9uAfcO31dv1g2AyN1Wg9NkU0wtE2w2A62bUQh27dUyNgMraMbJj51MDj7R4wTRMQCTKC5KbQaKKFonhQzTG3UQIbQrCD1K093NELz-MXlAgt9X9IvDVVY0TWDENS10UAy0tcsKwjiVIxA9jq6jaxa1sWO6zc6rDKEDxwKT3zPolJCRbbN2K-mnNLe7+FAA)

Use [template literal types](https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html) in typescript is very useful, this is a simple approach to use them.

### #4 Extract the column of where string
Now we will use `infer` and `conditional`to extract the which column that was specified 

Let's make a utility type for this
```ts
type ExtractColumnByWhere<Str> = Str extends MakeWhereString<infer C>
  ? C
  : never;
``` 
`infer` basically extracts from within another type structure, some type. Combining with conditional, we can return this type if typescript could infer the type correctly. [Read more about infer keyword](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#inferring-within-conditional-types).

So let's put all this into our structure and make the object with the values using `mapped types`
```ts
type QueryBuilderMethods<
  DB,
  Table extends keyof DB = keyof DB,
  Columns extends keyof DB[Table] = keyof DB[Table],
> = {
  //...
  where: <
    W extends MakeWhereString<Columns & string>,
    V extends ExtractColumnByWhere<W> 
  >(
    w: W,
    v: { [K in V]: DB[Table][V & Columns] }
  ) => QueryBuilderMethods<DB, Table>,
  //...
}
```
[Ts playground](https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgKoGdrIN4ChnLAAmAXMiAK4C2ARtANz7lxURnphSgDmjBEVOMAA27Tjz7IADnHToA7gHsopZBy4heTClKJxIRAPr6yAEX0RJCKBAtGTyc5EYBfXLlCRYiFAAUoikQUCGA4TMRklLQMTCAsbGrimpJEEOjWwFJgwIogYhpaBFJcSJHUdFBWNnbGYGYWru6e0PBIyADyKlh4BBHk5TEEFJhQhn1RFZLFgcFgY6oTg8gAjhRw4MBgAJ5l0ZVM1rYGtfXOuG64APSXaJgEYAAWwOiOAELIMMrIkBzouNtSFBOOCvWQQAAqaVCAF4wkMRmQMEtpkEQmR-DMQpJlKkoGROrjGrhUghhHAbMxWOgZG0AIoUaBbV4UES4uEfCggEI5EDIPRgOA0MEAHlMrwAfAAKIg0MyvACUZHpjOZrOgAFkII9AuhRRKiQCUMqoEyWcJcZrtURdUwxQAaJjgwXCFAQAAekBA1uQAGsIFtFDA3shYX6A0H7UwAMKKYTUEAvd2e71hwNvADaTpoLoAuiHff602LM86IDmHeL8z0PgEqGRheDkEmIF6XlmXVKBdmEuD5SHK8bTWqoJaHjq9XbkODxQ6CJgXWjkJL0GQAIJQKBwLbCmNxqgJ8V96EDhkm1XmjVasfWidT0szpjyB7QBLCpgEADqTY9Le96rgfofs+NgAMpJNwO6xvGLwAGSJAUD4EAQABq37Ji8ACiHqbiEu7xq8WxAS+wofpWTBSu+yDyGQH6zkhABuZDYMg6YANKELyyE5nKJbdjm6aoXBeH7ugeZuAQR4niqZoWle472ne3aIcgNAyWQkqScgGJUM8EDCvRijEOK5zuIayD-oBwEQGBBRvgQwm8s2rbwTwk5MO0gKbmAXxOd66g8PmMZUDIXDoLkHnQPoyjoG5KFwHGro-s5-maPmKXcLglawgABgAJNgDkuMg+URV5yhFSQ+XIfFDIuNljD-FsgLIEFIXPOFnlRVALywgARNCvXIAAPsgvXCuKg0jb14oDcNo3CgNDVmVhnCIGADkEURNjCjZWXIDZaG-i8FkQFt1ngcKoAwFgUbGQQAD8LVMJEED0TEuAILkHDIFQWymDQACSCYClyKCwtgRWyI4+ggmCkIcA1g7nriAB0-KCmCkq-f9QMcOsSDykwKMwLWkq9cM0C9YTBAo-OEAhJK6a9cQvWTr1hw1PorOjQIQjCL1ObU8gKNPi+ZO8yI+YkBL-OTsxMtkL1MsAAIyyjn1UINLhCyjqmshpQA)

The [Mapped types](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html) is a way to create new types of objects without repeating yourself, it iterates over the `union` of keys and creating the new object.

Works like this
```ts
type NewObjectKeys = "key" | "key1" | "key3"
type NewObject = { 
  [K in NewObjectKeys]: string
}
// Result in: {
//     key: string;
//     key1: string;
//     key3: string;
// }
```
[Ts playground](https://www.typescriptlang.org/play?ssl=10&ssc=1&pln=1&pc=1#code/C4TwDgpgBAchDuB5ARgKwgY2AaQiAzlALxQBEA1nqVAD5mUgCM1dFeAzKQFCiSwIp0WYlADeULlCgBtbFACWAO35I0mHHnwBdAFxR8wAE5KA5lwC+XAPRWx121KkM9B44pMBue46d5GLo1MvGx8oBnYAt09vSyA)

### #5 Make the `build` return
In the end, we need of the columns selected in the `.select()` methods, to make the return of our query builder.

Let's make a simple change to capture selected columns from generics
```ts
type QueryBuilderMethods<
  DB,
  Table extends keyof DB = keyof DB,
  Columns extends keyof DB[Table] = keyof DB[Table],
  // New Generics, based on the table columns
  ColumnsSelected extends Array<Columns> = Array<Columns>,
> = {
  from: <T extends Table>(table: T) => QueryBuilderMethods<DB, T>,
  // Here should be more specific, not just any columns array, but the columns array that is passed by params
  // and pass this columns again to return of .select() through generics of QueryBuilderMethods
  select: <S extends Array<Columns> = Array<Columns>>(s: S) => QueryBuilderMethods<DB, Table, Columns, S>,
  where: <
    W extends MakeWhereString<Columns & string>,
    V extends ExtractColumnByWhere<W> 
  >(
    w: W,
    v: { [K in V]: DB[Table][V & Columns] }
  // Don't forget of pass the generics to  `QueryBuilderMethods`
  ) => QueryBuilderMethods<DB, Table, Columns, ColumnsSelected>,
  // ...
}
```
[Ts playground](https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgKoGdrIN4ChnLAAmAXMiAK4C2ARtANz7lxURnphSgDmjBEVOMAA27Tjz7IADnHToA7gHsopZBy4heTClKJxIRAPr6yAEX0RJCKBAtGTyc5EYBfXLlCRYiFAAUoikQUCGA4TMRklLQMTCAsbGrimpJEEOjWwFJgwIogYhpaBFJcSJHUdFBWNnbGYGYWru6e0PBIyADyKlh4BBHk5TEEFJhQhn1RFZLFgcFgY6oTg8gAjhRw4MBgAJ5l0ZVM1rYGtfXOuG64APSXaJgEYAAWwOiOAELIMMrIkBzouNtSFBOOCvWQQAAqaVCAF4wkMRmQMEtpkEQmR-DMQpJlKkoGROrjGrhUghhHAbMxWOgZG0AIoUaBbV4UES4uEfCggEI5EDIPRgOA0MEAHlMrwAfAAKIg0MyvACUZHpjOZrOgAFkII9AuhRRKiQCUMqoEyWcJcZrtURdUwxQAaJjgwXCFAQAAekBA1uQAGsIFtFDA3shYX6A0H7UwAMKKYTUEAvd2e71hwNvADaTpoLoAuiHff602LM86IDmHQRrsgAHIQeTIADiEBA0GACHQduQQswRGQuW+DxQAuzKAQsfjfwIMbjVATAGUIC6QhBe0nm96AIJQKBwLbC6cT8X5rc7vcH2focUOo+wnofAJUMjC8HINdel5Zl1S4cusjg+UhkexqmmqUCWg8Op6p24JXkwVYABLQCg6AQRQ5pdigVDKMhgIIMAMBtp2ICKKEABWwyhOsWzIGOM4Jsg5Knp2NAUKEjyjuOF4Mduu4DvohAvDIcgrl21EyDuVCTsgVbrL2QkvI8zw0Zx9FwNwQi8mAijIDYYAUFAvJpgAdJgS5gJKAGPAEFDcA8yDcM2rbtn2QbAaq5oalqEHWkwpkQGiyDCnOr4euuLwnru+4qZex48We0XilK6BkHOAHQkBDImu5FpeZB9rIJ+ECdueCadnOsEEPIg42E+TAEAA6iFyYvOqcB+vV1UQHOSTcFFdEvAAZIkBQVQQyAAGpNWFyAAKIejuIQla8WwdUhwr1UeTBSnVyDyGQ9UVmNABuZDYMg6YANKELy405nKJYjjm6aTUNJXoHmbiVjcpi5AA5KEnxQA5oRpvJA4oA5LYlAp2nIAABm5Zo5Va6Bw0waUZSqSOeSjUEFaWxXRYT-ULmZK6jSxrJkBZgHIBiVDPBAwpHYoxDiuc7iGsgrXtZ13UFMK0YqVN77DTwnZMO0gI7lpUAi966g8PmMZUOJzy5FL0D6MoHbIEw41wHGrqhaLiuaPmZvcLgN7wwAJNgJUuMg9uazLyhOyQ9sG0bLhw4w-xbICyAq2r6Aa9L2tQC8sIAETQjHyAAD7IDHwrignycx+K8dJynwrx-7XNzZwiBgEtK2dUFnA2-z8stW1ECrTY-M8MKoAwFgUbswQAD8wdMJEEBHTEuBjgmoRUFspg0AAkuP6xtLeTuyI4+ggmCkIcP7Y8cAx+aI6BRn8oKYKSpP09zxwC8QPKTBGTAD6SjHwzQDHt8ECZi7+eZ6Yx8QMedhjocGo+gAEpwEEIYQMcczv2QEZKqSEn4QJEPmEgyCoGdjOugsgMd0EAAF0FGTHFQBOLhYFGUpuaCyQA)

Now that we have the columns selected, can use the `Pick` and `Indexed Access` to return object with only keys we selected in `.select()`

```ts
type QueryBuilderMethods<
  DB,
  Table extends keyof DB = keyof DB,
  Columns extends keyof DB[Table] = keyof DB[Table],
  ColumnsSelected extends Array<Columns> = Array<Columns>,
> = {
  // ...,
  build: () => Promise<Pick<DB[Table], ColumnsSelected[number]>>
}
```
[Ts playground](https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgKoGdrIN4ChnLAAmAXMiAK4C2ARtANz7lxURnphSgDmjBEVOMAA27Tjz7IADnHToA7gHsopZBy4heTClKJxIRAPr6yAEX0RJCKBAtGTyc5EYBfXLlCRYiFAAUoikQUCGA4TMRklLQMTCAsbGrimpJEEOjWwFJgwIogYhpaBFJcSJHUdFBWNnbGYGYWru6e0PBIyADyKlh4BBHk5TEEFJhQhn1RFZLFgcFgY6oTg8gAjhRw4MBgAJ5l0ZVM1rYGtfXOuG64APSXaJgEYAAWwOiOAELIMMrIkBzouNtSFBOOCvWQQAAqaVCAF4wkMRmQMEtpkEQmR-DMQpJlKkoGROrjGrhUghhHAbMxWOgZG0AIoUaBbV4UES4uEfCggEI5EDIPRgOA0MEAHlMrwAfAAKIg0MyvACUZHpjOZrOgAFkII9AuhRRKiQCUMqoEyWcJcZrtURdUwxQAaJjgwXCFAQAAekBA1uQAGsIFtFDA3shYX6A0H7UwAMKKYTUEAvd2e71hwNvADaTpoLoAuiHff602LM86IDmHQQY3GqAmAMoQF0hCBEZBJiBel4AQSgUDgW2FVfj6HF+e7vf7g5rw4dI9hPQ+ASoZGF4NbHvb3qzLqlAuzCXB8pDI+NprVUEtDx1ertyHB4oragbEDRyGFtbXya7Pb7A9jQ9nyBjj+k4JuKUroGQtaHtCx4MiaqrmhqWqXta163qWN4gegN61veTDyA80AJMKTAEAA6h+G4vOqcB+mRhE2LWSTcL+1YJsgABkiQFHhBAEAAapRHbIAAoh6vYhCBrxbPRRHCmRI5MFKpHIPIZBkQ+BAAG5kNgyDpgA0oQvL8TmcolnuObpoJXFYXmbgENBsEqmaFrIVe9roXumF-lOPlseg9aNgYvHIDQrlkJKTnIBiVDPBAwq+MACA+nqFm5v5Q5Bc+BjposUA5mB5zuIayA0XRDEQExBQkZWvm8m2wnqDwN5MO0gK9mAXyNd6zWaPmMZUDIXDoLk7XQPoyjYcgTD8XAcauuuTXMfmfXcLgAEAAYACTYCBLjILt42dcoB0kLtc0LS4m2MP8WyAsgg3Dc8Y0dZNUAvLCABE0JfcgAA+yBfcK4p-YDX3ir9ANA8Kv23aVYmcIgYBSTJlVvpwAHVUJ3rlRAsmMcxwqgDAWBRuKTAAPyPUwkQQFpMS4AguQcMgVBbKYNAAJIJgKXIoHOB2yI4+ggmCkIcLdzO88gcD5ieCG4gAdPygpgpK7OczzHDrEg8pMErMCLpKX3DNAX36wQSuYMFkrpl9xBfTeX2HDU+hO0DAhCMIX05pbyBKwRREm17Ij5iQoc+zeemR2QX2RwAApHSvM1Qf0uP7SvhayUVAA)
What a workaround this is?
I confess that this is not at all pretty, but I thought this approach was nice.

The `Indexed Access` we can get a specific type from an indexed type. How `ColumnsSelected` is a array, we can access all its elements by accessing your `number` property.

Some examples
```ts
const arrayNumbers = [1, 2, 3, 4, 5] as const;
type ArrayNumbers = typeof arrayNumbers

type SpecificNumbers = ArrayNumbers[number] // 1 | 2 | 3 | 4 | 5
type SpecificNumbersIndex = ArrayNumbers[2] // 3

// Works with object
interface User {
  id: number;
  name: string;
  email: string;
  password: string;
  updated_at: Date;
}
type UserEmail = User["email"] // string
```
[Ts playground](https://www.typescriptlang.org/play?ssl=14&ssc=41&pln=6&pc=1#code/MYewdgzgLgBAhgJwXAngOQK4FsBGBTBCGAXhgG0BGAGhgCYaBmGgFhoFYBdeI0SKAbgBQUFAAc8MAIJJUmXASKkR4kADN4M9NnyFhYiQGVxwAJaqTwOTsVTNVhWTDaCHPeJhG8p85eeEAkmAAJngAHiS2yFryhGS0roIA9IkwAOogCADWRADuJlAAFjAgOABWXlCCJmBQBKpwwBIAqhAEMADegjAwJkEAXDBOMULdYHBYeAPQCNUA5iMweFhwJgA2U1AzYPNdMKJwEBA5Gf0w03MLGKJBcLVBAPq3AwAit3hCAL5uza0IAKLLNYRFoEMgAIiWK1WYK4yTOmzmQA)


That's it. Now we have a query builder fully typed and you probably won't get the column syntax wrong again.
```ts
const user = QueryBuilder.database(myDbInstance)
  .from("user")
  .select(["id", "created_at", "email"]) // Only accept the user keys
  .where("email = :email", { email: "email@email.com" }) // String only accept correct user table keys, specific Operators and specific object as second parameter 
  .build()
// Returns ->
// {
//   id: number;
//   email: string;
//   password: string;
// }
```
Feel free to test the code [here](https://www.typescriptlang.org/play?#code/MYewdgzgLgBAtgTwCICMCSkoEMzAKYwC8MA3gL4xYQxJbYBCVeAKntANwBQnomlRMAIoBXPACcE9YQEsANgBNxAOnl0sKJgApEqDNBz4AlJxgwlAMzEg4mgETCI428dNLHsvMCiaA2renytgA0MLbAYnh0ePIA+nTBoXhwWHK2ALouZgDuABbieHZJKbICAFxFqSEkMBWypYnJcgACtUqgcLYwZJlKKDIKmsac0mBQ4uZY+DAAqo5ipCYwAfVgwnAo4lymYFhwePXQYiMA5ls1jXUwhydnAA5UEFkgYvIHUEdgp4vCt6pjsXR6rQxmdwpF-nEoEColwyNwRmMxBMpgAFKzyYReBamZYwVbrTaLHZ7N4fL6mRQQcLSW5QaTgUk3Ra3I74FZrDZiUERKIAqE0GGcOHDUbjSYEADyL3E2KWrzxHMJpgc4hiuPxnLu6MxUDV8o1SpgAEdhDg6VAEOyCVzFmDeZDoSChdwLbcCMCsIxHKxoAISN85vVZoaWSAMV56miwzqzs9FGJ6lL47DuIpgLIsBE8bs2PcpiJxJJ+vHZeZhLg6eAYH91EwADxIegAPk08hQQPohnqBYkUjk8YAsngoDkwxAG82U5xXQQe0X++IhyOx3XFo2gotmOoPDUAB5jMDyagAazwCBA5ho9AEp-Pl-XiwAwiBZGtIHuD0eYLeL1efFuUA8NIbzPX9G3-bc8DSDdTGfV84EgABlPAPC8aIPzwQ9qAAQTEMQsAQOs4LfCAmwEXD8MI4iENIjcyOIf1TEsax6jrZgMKwmAAI8FtsEA-YuMMIgyLnPsFEXYdRyPCcQmYJsYKuFDPH5OtEI4r8KIIoiXxI+iYE0qidJopsWwgepEKEwgRNEXtiwk5dpPXLjIJCajIBCRD5MWXJ8lYxZTAAdXU6gBywU8AryCJEPeE5tPg98ADIrhiz4vNMUwADVgpgABRfd8K8Nz6AQCL8jrAKyMWFt-JgLJ6gChTTAAN3qaofAAaSWMAYAytIOwg-i0h8LKkrciBgLhUxLOswsxMHSSVyc7i8Fcoz3JgMbkNQ-40pgPp+3qQZhJgKM4GkRw6xRaRgGPCcBqA1b4ogLblOiHwDTENITOdacEDdGBQvCyK8GislV1gtbsuuT4QkWCU3XwqBnihlLjgEZ84HuI4IHAeHxDoZ4IFhzKsFfAg8H3TCv2htHiBpzg9IAAwAEhINyKFZvHEeeChSlZjLSdEMhGa4X7-oxrHztxhGCbEahiFsQhOgAH1COsmxV0ImyVmBVdsOsldFmdcvyyYoCKkrgdU949NB7LAbwUqotRusRnMGVHybRYAH4NsWFY8CazYgA)
[Gist](https://gist.github.com/Luisgustavom1/cdc154c180f3969efdabb9d383ff9e26)

## Conclusion
I hope you understood and left here with some new knowledge.

That's it guys, I want to bring more in-depth content on these subjects and other things from the world of technology.

This month I will create a routine of bringing you some articles every week