Mapeando objetos com o `in keyof` no Typescript
Published: 2023-02-15 14:27:40

Neste artigo vamos aprender como funciona o operador `in keyof` do Typescript, e como ele pode te ajudar a mapear o tipo de objetos, assim evitando de redeclarar propriedades de objeto

## Entendendo o `in keyof`

### keyof

Antes de tudo, precisamos saber, o que é o `keyof`? No Typescript ele é utilizado para obter o nome das propriedades publicas de um tipo.

```typescript
type Person = {
  age: number
  name: string
}

keyof Person // age | name
```

### in keyof

`in` é utilizado quando você quer definir uma assinatura de indicie que podemos fornecer para um tipo. Em combinação com o keyof, podemos utilizar para declarar as propriedades que um tipo vai ter baseado nas propriedades de outro tipo.

Podemos criar um objet:

```typescript
type ToBoolean<T> = {
  [K in keyof T]: boolean
}

type Product = {
  name: string
  price: number
}

const hasProductProperties: ToBoolean<Product> = {
 name: true,
 price: false
}
```

## Elevando o seu conhecimento.

### extends keyof

`extends` basicamente funciona como em classes, você extende propriedades de um tipo. Em combinação com o keyof, você restringe os valores que podem ser passados para um tipo genérico.


```typescript
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}

const person: Person = {
  age: 26,
  name: "Gustavo",
}


const name = getProperty(person, "name")
```

Nesse exemplo, eu estou definindo a função getProperty, que recebe como primeiro parametro um objeto, e segundo a chave desse objeto, retornando o valor dessa propriedade.

### Combinando o `in keyof` e o `extends keyof`


```typescript
// bloco 1
type Query<T, K extends keyof T> =  {
  $eq?: T[K] | Array<T[K]>
  $ne?: T[K] | Array<T[K]>
}

// bloco 2
type WhereParams<T> = {
  [K in keyof T]?: T[K] | Query<T, K>
};

// bloco 3
type Product = {
  name: string
  price: number
}

// bloco 4
const myQuery: WhereParams<Product> = {
  name: {
    $eq: 'Gustavo'
  }
}

```

- Bloco 1:

Estou dizendo que os filtros devem ter o tipo da devida propriedade do objeto que eu passar.

exemplo com o `Product`, somente string | number

```typescript
// erro
const myQuery: Query<Product, keyof Product> = {
  $eq: false
}

// sucesso

const myQuery2: Query<Product, keyof Product> = {
  $eq: 'uma string'
}

```

- Bloco 2:

O WhereParams recebe um tipo, e as propriedades dele, irão ser assinadas com as chaves do tipo que eu passei como generic. Sendo o valor desse objecto, do tipo da propriedade em específico, ou um objeto de queries desse mesmo tipo.



- Bloco 3: 

Definição da entidade Produto

- Bloco 4:

Instancia da query baseada nos filtros da entidade `Product`.

## Conclusão

Existem maneiras de deixar nosso código muito mais inteligênte elevando o auto complete à um outro nível. Com o `in keyof`, podemos criar interfaces genéricas mapeando toda cadeia de propriedades, baseando tipos em outros tipos, evitamos a repetição de código.

## Referências

- [Mapped Types](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html)
- [Indexed Access Type](https://www.typescriptlang.org/docs/handbook/2/indexed-access-types.html)