Um guia abrangente para entender o tipo de registro do TypeScript – SitePoint


TypeScript’s Record O tipo simplifica o gerenciamento de estruturas de objetos com tipos de valor consistentes. Este guia abrange o essencial de Recordincluindo sua definição, sintaxe e como ela difere de outros tipos, como tuplas. Vamos aprender a definir e usar Record Em cenários práticos, como aplicar o manuseio exaustivo de casos e o mapeamento de enumes. Além disso, exploraremos usos avançados combinando Record com tipos de utilidade como PartialAssim, Picke Readonly.

Introdução

O Record O tipo é um tipo de utilidade que nos permite criar um tipo de objeto com teclas especificadas e um tipo de valor uniforme. Esse tipo é particularmente útil para definir mapeamentos e garantir que todos os valores em um objeto estão em conformidade com um único tipo.

Definição do tipo de registro

A definição oficial do Documentação do TypeScript é:

Record<Keys, Type>

Aqui:

  • Keys Representar o conjunto de chaves no registro, que pode ser uma união de literais de cordas ou um tipo derivado de uma união.
  • Type é o tipo de valores associados a essas teclas.

Por exemplo, Record Define um objeto em que cada chave é uma string e todo valor é um número. Esse tipo garante que todas as propriedades do objeto tenham o mesmo tipo de valor, mas as teclas podem ser variadas.

Comparação entre um registro e uma tupla

Ambos Record e Tuplas são usados ​​para lidar com coleções de dados, mas servem a propósitos diferentes. Mesmo quando armazenam vários valores, eles diferem em estrutura e uso. Um registro nomeou propriedades com um tipo fixo, enquanto uma tupla é uma lista ordenada de elementos identificados por sua posição. Aqui está uma comparação simples:

  • Registro. Cria um tipo de objeto em que todos os valores têm o mesmo tipo, mas as teclas podem ser flexíveis. Isso é útil para mapear as teclas para valores e garantir que todas as teclas sigam um tipo específico.
  • Tupla. Define uma matriz com um número fixo de elementos, onde cada elemento pode ter um tipo diferente. As tuplas são usadas quando precisamos de uma coleção de tamanho fixo com tipos específicos para cada posição.

Por exemplo, considere o seguinte.

Aqui está um Record Tipo, que mapeia as teclas da string para os valores do número:

type AgeMap = Record<string, number>;

Um tipo de tupla representa uma matriz com uma string (nome) e um número (idade) em uma posição fixa:

type Person = (string, number);

Uso básico do tipo de registro

O Record O tipo fornece uma maneira simples e eficiente de mapear as teclas para os valores. É particularmente útil quando precisamos definir objetos com pares de chave -chave específicos, onde as teclas são de um tipo específico e os valores são de outro tipo.

Aqui estão algumas maneiras básicas de usar o Record Digite para definir e criar dados estruturados.

Definindo um registro

Para definir um Recordespecificamos os tipos para as chaves e valores. O exemplo abaixo define um objeto em que cada tecla é uma string e cada valor também é uma string. Isso pode ser usado para um mapa genérico de dados do usuário:

type User = Record<string, string>;

Criando um Record Exemplo de tipo

Alguns sites têm vários subdomínios. Vamos supor que cada um desses subdomínios requer algum nível de acesso ao administrador e crie um Record Digite para armazenar diferentes funções de administrador e seus níveis de acesso correspondentes. Aqui, UserRoles e UserStatus são Record Tipos em que as chaves são literais específicos de cordas (adminAssim, blogAdminAssim, docsAdminAssim, activeAssim, inactiveAssim, suspended), e os valores são seqüências que descrevem cada função e status.

Primeiro, definimos um Record tipo UserRoles com funções de administrador específicas como chaves e suas descrições como valores. O UserRoles Tipo garante que qualquer objeto desse tipo tenha chaves adminAssim, blogAdmine docsAdmincom valores de string descrevendo cada função. O roles Objeto adere a esse tipo, fornecendo descrições para cada função de administrador:

type UserRoles = Record<'admin' | 'blogAdmin' | 'docsAdmin', string>;

const roles: UserRoles = {
  admin: 'General Administrator with access to all areas.',
  blogAdmin: 'Administrator with access to blog content.',
  docsAdmin: 'Administrator with access to documentation.'
};

Em seguida, definimos um Record tipo UserStatus com status específicos como chaves e suas descrições como valores. O UserStatus Tipo garante que qualquer objeto desse tipo tenha chaves activeAssim, inactivee suspendedcom valores de string descrevendo cada status. O userStatus Objeto adere a esse tipo, fornecendo descrições para cada status:

type UserStatus = Record<'active' | 'inactive' | 'suspended', string>;

const userStatus: UserStatus = {
  active: 'User is currently active and can use all features.',
  inactive: 'User is currently inactive and cannot access their account.',
  suspended: 'User account is suspended due to policy violations.'
};

Criando Record Tipos dessa maneira, garantimos que as funções de administrador e os status do usuário sejam bem definidas e consistentes durante todo o aplicativo.

Casos de uso prático do tipo de registro

Nesta seção, revisaremos vários casos de uso prático do Record Digite para demonstrar sua versatilidade e eficácia em diferentes cenários.

Usar Caso 1: Exibindo o manuseio de casos exaustivos

Usando Record Definir um mapeamento entre valores e mensagens de caso nos permite lidar com cada caso possível explicitamente. Isso garante que todos os casos sejam cobertos e que quaisquer casos ausentes resultem em erros de tempo de compilação.

No exemplo abaixo, statusMessages é a Record onde as chaves são específicas Status valores ('pending'Assim, 'completed'Assim, 'failed') e cada chave de chave para uma mensagem correspondente. O getStatusMessage A função usa este registro para retornar a mensagem apropriada com base no status parâmetro. Essa abordagem garante que todos os status sejam tratados de maneira correta e consistente.

Exemplo:

type Status = 'pending' | 'completed' | 'failed';

interface StatusInfo {
  message: string;
  severity: 'low' | 'medium' | 'high';
  retryable: boolean;
}

const statusMessages: Record<Status, StatusInfo> = {
  pending: {
    message: 'Your request is pending.',
    severity: 'medium',
    retryable: true,
  },
  completed: {
    message: 'Your request has been completed.',
    severity: 'low',
    retryable: false,
  },
  failed: {
    message: 'Your request has failed.',
    severity: 'high',
    retryable: true,
  },
};

function getStatusMessage(status: Status): string {
  const info = statusMessages(status);
  return `${info.message} Severity: ${info.severity}, Retryable: ${info.retryable}`;
}


console.log(getStatusMessage('completed')); 

Caso de uso 2: Exibindo a verificação do tipo em aplicativos usando genéricos

Os genéricos no TypeScript permitem código flexível e reutilizável. Quando combinado com Recordos genéricos podem ajudar a aplicar a verificação do tipo e garantir que os objetos estejam em conformidade com estruturas específicas.

Usando genéricos com Recordpodemos criar funções ou utilitários que geram objetos com um conjunto específico de chaves e um tipo de valor consistente. Essa abordagem aprimora a segurança e a reutilização do tipo em nossa base de código.

No exemplo abaixo, o createRecord a função leva uma variedade de chaves e um valor, e retorna um Record onde cada chave mapeia para o valor fornecido. Esta função usa genéricos (K para chaves e T para o tipo de valor) para garantir que o resultante Record tem a estrutura correta.

Exemplo:

function createRecord<K extends string, T>(keys: K(), value: T): Record<K, T> {
  const record: Partial<Record<K, T>> = {};
  keys.forEach(key => record(key) = value);
  return record as Record<K, T>;
}

interface RoleInfo {
  description: string;
  permissions: string();
}

const userRoles = createRecord(('admin', 'editor', 'viewer'), {
  description: 'Default role',
  permissions: ('read'),
});

console.log(userRoles);

Caso de uso 3: Mapeamento enums para dados

Usando Record Mapear enums aos dados nos permite criar uma tabela de pesquisa onde cada valor de enumeração está associado a informações específicas. Isso é particularmente útil para cenários como definir configurações com base nos valores da enumeração.

Neste exemplo, colorHex é a Record isso mapeia cada um Color Valor de enumeração ao seu código de cores hexadecimal correspondente. Essa abordagem fornece uma maneira clara e segura de tipo de lidar com dados relacionados a cores com base nos valores da enumeração.

Exemplo:

enum Color {
  Red = 'RED',
  Green = 'GREEN',
  Blue = 'BLUE',
  Yellow = 'YELLOW'
}

interface ColorInfo {
  hex: string;
  rgb: string;
  complementary: string;
}

const colorHex: Record<Color, ColorInfo> = {
  (Color.Red): {
    hex: '#FF0000',
    rgb: 'rgb(255, 0, 0)',
    complementary: '#00FFFF',
  },
  (Color.Green): {
    hex: '#00FF00',
    rgb: 'rgb(0, 255, 0)',
    complementary: '#FF00FF',
  },
  (Color.Blue): {
    hex: '#0000FF',
    rgb: 'rgb(0, 0, 255)',
    complementary: '#FFFF00',
  },
  (Color.Yellow): {
    hex: '#FFFF00',
    rgb: 'rgb(255, 255, 0)',
    complementary: '#0000FF',
  },
};

console.log(colorHex(Color.Green)); 

Use Caso 4: Criando tabelas de pesquisa

Uma tabela de pesquisa usando Record Ajuda no mapeamento de chaves (como identificadores, nomes) para valores específicos (como descrições, códigos). Isso pode ser útil para várias aplicações, incluindo configurações, traduções e muitas outras coisas.

Aqui, countryCode é a Record Isso mapeia os códigos do país para seus respectivos nomes de países, população, capitais e continentes. Esta tabela de pesquisa permite uma recuperação rápida e segura de tipo de nomes e populações de países com base em códigos de país.

Exemplo:

type CountryCode = "US" | "CA" | "MX" | "JP";

interface CountryInfo {
  name: string;
  population: number;
  capital: string;
  continent: string;
}

const countryLookup: Record<CountryCode, CountryInfo> = {
  US: {
    name: "United States",
    population: 331000000,
    capital: "Washington D.C.",
    continent: "North America",
  },
  CA: {
    name: "Canada",
    population: 37700000,
    capital: "Ottawa",
    continent: "North America",
  },
  MX: {
    name: "Mexico",
    population: 128000000,
    capital: "Mexico City",
    continent: "North America",
  },
  JP: {
    name: "Japan",
    population: 126300000,
    capital: "Tokyo",
    continent: "Asia",
  },
};

console.log(countryLookup.US);


console.log(countryLookup.US.population);

Iterando Record Tipos

Iterando Record Os tipos são importantes para acessar e manipular os dados nas estruturas de dados. Vamos criar dados de amostra e mostrar vários métodos sobre como podemos iterar sobre o datilograma Record tipos.

Dados de amostra:

interface Course {
  professor: string;
  credits: number;
  students: string();
}

interface Courses {
  (key: string): Course;
}

const courses: Courses = {
  Math101: {
    professor: "Dr. Eze",
    credits: 3,
    students: ("Emmanuel", "Bob", "Charlie"),
  },
  History201: {
    professor: "Dr. Jones",
    credits: 4,
    students: ("Dave", "Eve"),
  },
};

Usando forEach. Para usar forEach com um Recordconverta-o em uma variedade de pares de valor-chave:

Object.entries(courses).forEach(((key, value)) => {
  console.log(`${key}: ${value.professor}, ${value.credits}`);
  value.students.forEach(student => {
    console.log(`Student: ${student}`);
  });
});


Usando for...in. O for...in Loop itera sobre as chaves de um Record:

for (const key in courses) {
  if (courses.hasOwnProperty(key)) {
    const course = courses(key);
    console.log(`${key}: ${course.professor}, ${course.credits}`);
    course.students.forEach(student => {
      console.log(`Student: ${student}`);
    });
  }
}


Usando Object.keys(). Object.keys() retorna uma matriz do RecordKeys:

Object.keys(courses).forEach((key) => {
  const course = courses(key);
  console.log(`${key}: ${course.professor}, ${course.credits}`);
  course.students.forEach(student => {
    console.log(`Student: ${student}`);
  });
});


Usando Object.values(). Object.values() retorna uma matriz do Recordvalores:

Object.values(courses).forEach((course) => {
  console.log(`${course.professor}, ${course.credits}`);
  course.students.forEach(student => {
    console.log(`Student: ${student}`);
  });
});


Tipos avançados de uso e utilidade com Record

O Record O tipo pode ser combinado com outros tipos de utilidade para obter maior flexibilidade e segurança do tipo. Esta seção expõe padrões de uso avançado, demonstrando como Record pode trabalhar com tipos de serviços públicos como PickAssim, Readonlye Partial.

Combinando Record com Pick Para mapeamento de tipo seletivo

O Pick O tipo de utilidade nos permite criar um novo tipo selecionando propriedades específicas de um tipo existente. Isso é útil quando queremos trabalhar com apenas um subconjunto de propriedades de um tipo maior.

Aqui, criamos um novo tipo SelectedProductInfo escolhendo apenas o name e price propriedades do ProductInfo interface, e depois usando Record Para mapear produtos diferentes para este novo tipo:

interface ProductInfo {
  name: string;
  price: number;
  category: string;
}
type SelectedProductInfo = Pick<ProductInfo, "name" | "price">;
type Product = 'Laptop' | 'Smartphone' | 'Tablet';

const products: Record<Product, SelectedProductInfo> = {
  "Laptop": { name: "Dell XPS 15", price: 1500 },
  "Smartphone": { name: "iPhone 12", price: 999 },
  "Tablet": { name: "iPad Pro", price: 799 }
};

Combinando Record com Readonly Para propriedades imutáveis

O Readonly O tipo de utilidade garante que as propriedades não possam ser modificadas depois que elas serem definidas. Isso é útil para criar estruturas de dados imutáveis.

O ReadonlyProductInfo digite o exemplo abaixo faz todas as propriedades de ProductInfo Imutível, garantindo que os detalhes de cada produto não possam ser alterados depois de definidos:

type ReadonlyProductInfo = Readonly<ProductInfo>;
const readonlyProducts: Record<Product, ReadonlyProductInfo> = {
  "Laptop": { name: "Dell XPS 15", price: 1500, category: "Electronics" },
  "Smartphone": { name: "iPhone 12", price: 999, category: "Electronics" },
  "Tablet": { name: "iPad Pro", price: 799, category: "Electronics" }
};

Combinando Record com Partial Para propriedades opcionais

O Partial O tipo de utilidade torna todas as propriedades de um tipo opcional. Isso é útil para cenários em que nem todas as propriedades podem ser conhecidas ou necessárias ao mesmo tempo.

Aqui, o PartialProductInfo Tipo nos permite criar produtos com algumas ou nenhuma das propriedades definidas em ProductInfofornecendo flexibilidade na forma como as informações do produto são especificadas:

type PartialProductInfo = Partial<ProductInfo>;
const partialProducts: Record<Product, PartialProductInfo> = {
  "Laptop": { name: "Dell XPS 15" },
  "Smartphone": { price: 999 },
  "Tablet": {}
};

Combinando Record com Record para mapeamento aninhado

Outro uso avançado envolve combinar Record Tipos para criar mapeamentos aninhados, que podem ser particularmente úteis para gerenciar estruturas de dados complexas.

Neste exemplo, storeInventory usa aninhado Record tipos para mapear os departamentos para seus respectivos produtos e detalhes, demonstrando como Record pode ser combinado para gerenciamento de dados mais complexo:

type Department = 'Electronics' | 'Furniture';
type ProductDetails = Record<Product, ProductInfo>;

const storeInventory: Record<Department, ProductDetails> = {
  "Electronics": {
    "Laptop": { name: "Dell XPS 15", price: 1500, category: "Electronics" },
    "Smartphone": { name: "iPhone 12", price: 999, category: "Electronics" },
    "Tablet": { name: "iPad Pro", price: 799, category: "Electronics" }
  },
  "Furniture": {
    "Chair": { name: "Office Chair", price: 200, category: "Furniture" },
    "Table": { name: "Dining Table", price: 500, category: "Furniture" },
    "Sofa": { name: "Living Room Sofa", price: 800, category: "Furniture" }
  }
};

Conclusão

O Record O tipo é uma ferramenta versátil para gerenciar e estruturar os tipos de objetos, pois nos permite definir mapeamentos claros entre chaves e valores, garantindo a segurança e a consistência do tipo em nosso código.

Para obter informações mais detalhadas, consulte o Documentação do TypeScript e revisar outros recursos adicionais, como Typatript total e Tutorial de texto datilografado Para aprofundar sua compreensão do datilografado Record Sistema de tipo.



Source link