Tipos genéricos de TypeScript como parâmetros

Jeff Gilliland Blocked Unblock Seguir Seguindo 1 de janeiro

Na constante busca de seguir os princípios do DRY e criar o código dinâmico e reutilizável perfeito, você provavelmente se deparará com o conceito de genéricos. Embora não seja um conceito novo no mundo da programação, certamente é se você estiver escrevendo JavaScript desde os anos 90. O TypeScript é um maravilhoso super conjunto que nos permite usar este conceito de genéricos para escrever código reutilizável, dinâmico e bonito.

Frequentemente, você encontrará uma situação em que você tem vários modelos de dados para representar seu aplicativo. Por exemplo, digamos que estamos criando um aplicativo que rastreie várias empresas, os produtos que eles vendem e os compradores que compram esses produtos. Criaríamos alguns modelos de TypeScript para representar essas várias classes de dados:

Exemplos de modelos para nosso aplicativo de rastreamento de produtos

Para os propósitos deste exemplo, adicionei alguns suportes estáticos a cada uma dessas classes. Eu defini strings “displayProp” específicas que devem ser usadas para determinar qual propriedade de modelo é exibida. Além disso, tenho uma propriedade “tableName” que você pode imaginar como o nome da tabela de banco de dados a que esse modelo se refere.

Agora, queremos ter uma lista de todas as nossas empresas, produtos e compradores para esses produtos. Poderíamos criar um componente de lista específico para cada Empresa, Produto e Comprador, mas haveria muita duplicação de código apenas para exibir uma lista para cada tipo de modelo. Isso seria insustentável à medida que a complexidade do nosso aplicativo cresce e queremos exibir mais tipos de dados em uma lista!

Criar um componente de lista dinâmica

Em vez de criar uma lista para cada tipo de dados, podemos criar um Componente Genérico que não se preocupa com o tipo de dados. Vou usar o Angular para este exemplo, mas os conceitos são aplicáveis em outros frameworks (e no VanillaJS também!).

Um componente de lista genérica

Você notará aqui que definimos uma classe genérica adicionando o parâmetro <T> ao nome da classe. Isso nos permite usar o tipo genérico em nossas várias propriedades de classe. No entanto, temos nomes diferentes para os nossos vários “nomes” de modelos (ou seja, nome do produto, nome da empresa, nome do comprador, etc.), portanto, como devemos referenciar essas propriedades em nosso elemento <h4>?

Precisamos usar nosso tipo genérico como se fosse um parâmetro!

Você notará uma propriedade especial no meu componente aqui que se destina a ser uma referência ao tipo genérico que podemos usar dinamicamente como se fosse o mesmo que um objeto real:

 const modelType: {new (... args: qualquer []): T; }; 

A sintaxe é estranha, mas essencialmente estamos dizendo que espera-se que o modelType seja um objeto genérico onde a nova palavra-chave pode receber qualquer número de propriedades.

Isso nos permite criar dinamicamente um novo objeto do tipo genérico em tempo real em nosso componente genérico:

 const newGenericObj = new modelType ({}); 

Para nosso exemplo, precisamos acessar a propriedade static para nossa propriedade de exibição no modelo (displayProp estático) na tag <h4> para que não precisemos criar um novo objeto genérico, simplesmente passar uma referência para o genérico digite em nosso componente de lista genérica:

Exemplo de uso de nosso componente de lista genérica com tipos genéricos

Para obter mais robustez, podemos criar uma interface que defina essa configuração de parâmetro de tipo genérico e também definir quais propriedades estáticas esperamos de nosso modelo:

Uma interface reutilizável que descreve nosso esquema de parâmetros genéricos

Podemos, então, refatorar o exemplo anterior para que nossa propriedade "modelType" seja agora do tipo "IGenericModel <T>".

Este não é o único caminho também. Talvez a criação dessas propriedades "ref" no exemplo anterior pareça excessivamente detalhada. Existe outra maneira de definir nossa propriedade modelType:

 // (modelo de dados é o nome da classe do seu modelo) 
modelType: <T> ({}) => T;

Isso está dizendo que modelType será uma função que retorna nosso tipo genérico (essencialmente dizendo o mesmo que a nossa definição anterior). Isso nos permite simplesmente passar a referência de tipo para o componente generic-list, para que, em vez de fazer isso:

 [modelType] = "productRef" 

Em vez disso, podemos apenas passar o tipo de classe real:

 [modelType] = "Produto" 

Os genéricos tendem a ficar um pouco confusos, mas uma vez que você pega o jeito da sintaxe e do conceito geral, você pode começar a aplicar o conceito através da placa para tornar os dados dos seus componentes de interface do usuário agnósticos. Espero que o conceito de usar tipos como parâmetros o ajude a escrever componentes genéricos mais dinâmicos!