Pesquisa de site

Como agrupar uma família de tipos relacionados em TypeScript


Então, no meu aplicativo, eu tenho um componente de grade que é usado para alguns tipos diferentes de dados.

Todos os tipos de dados são obtidos através de diferentes endpoints de API, usando diferentes opções de filtragem, etc. Tudo isso é devidamente definido com tipos apropriados e funciona bem quando estou trabalhando com um tipo específico.

Como posso "agrupar" todos esses tipos juntos por "família"?

Eu gostaria de poder descrever a "forma" de uma família (por exemplo, ela tem um tipo Filtro, um tipo Resposta, etc.) e ser capaz de impor o relacionamento (por exemplo, quando temos esse tipo de dado, precisamos desse tipo de filtro).

Eu tentei várias abordagens usando interfaces e classes, mas não consigo especificar um tipo com um "." nele; isso é reservado para namespaces.

Namespaces são promissores, mas eu não posso impor a "forma" deles, então eu não posso fazer meu componente usar um namespace "abstrato" comum.

Alguma ideia sobre a abordagem que eu deveria usar?

EDITAR: Para esclarecimento, aqui está o que eu gostaria de fazer:

type View<D, F> = {
  // these should be the actual types, not an instance of
  Data: D;
  Filter: F;
}
type TasksView = View<Task, TaskFilter>;
type ProjectsView = View<Project, ProjectFilter>;

interface GridProps<T extends View> {
  filters: T.Filter;
}
function Grid<T>(props: GridProps<T>) {
  const getData(props.filters): T.Data {
    // return data
  }
  return (
    <table>
    </table>
  )
}

function MyTasksGrid = Grid<TasksView>
function MyProjectsGrid = Grid<ProjectsView>

Não é 100% sintaticamente correto, mas espero que ilustre o conceito. Basicamente, eu quero "vincular" os tipos Task e TaskFilter através de uma estrutura comum.

No entanto, T.Data como descrito acima não funciona, pois T não é um namespace ('T' refere-se apenas a um tipo, mas está sendo usado como um namespace aqui).

Resposta #1

Quando você precisa criar uma interface "comum" para objetos que têm alguns campos em comum enquanto outros são diferentes, você pode usar a notação | e definir um novo type. Veja o exemplo abaixo:

//Two similar objects with partially common interfaces
interface Bird {
  name: string
  age: number
  type: 'BIRD'
  //Different
  feathers_color: string
  fly: ()=>void
}
 
interface Fish {
  name: string
  age: number
  type: 'FISH'
  //Different
  fins: number
  swim: ()=>void
}

type Animal = Fish | Bird;

let my_animal: Animal = {
  name: 'Karl'
  age: 8
  type: 'FISH' as 'FISH'
  fins: 3
  swim: () => { console.log("I'm swimming...") }
}

//Properties that are accessible without verifying if the animal is a bird or a fish
console.log(my_animal.name);
console.log(my_animal.age);
console.log(my_animal.type);
if(my_animal.type === 'FISH'){
  //Properties that are accessible only after checking the type
  console.log(my_animal.fins);
  my_animal.swim(); //Typescript will not complain here
}

Aviso: quando você criar um objeto, você deve especificar o parâmetro type manualmente.

Aviso 2: essa abordagem geralmente não funciona bem (em termos de manutenção) quando você precisa de múltiplos níveis de agrupamento. Você terá que definir vários campos type para cada grupo e isso pode se tornar bagunçado muito rapidamente.

ATUALIZAÇÃO Esta deve ser a implementação do que você pediu usando o método descrito acima

interface Task{}
interface TaskFilter{}
interface Project{}
interface ProjectFilter{}

interface TaskView {
  Data: Task
  Filter: TaskFilter
}

interface ProjectView{
  Data: Project
  Filter: ProjectFilter
}

type View = TaskView | ProjectView

interface GridProps<T extends View> {
  filters: T['Filter'];
}

//Alternative 1: Without grid props, that wrapper seemed excessive for the definition of the type
function Grid<T extends View>(props: T) {
  function getData(filter: T['Filter']): T['Data'] {
    // return data
    return {}
  }
  return (
    <table>
    </table>
  )
}

//Alternative 2: maybe I'm missing something, so I add also a version with Grid Props
function Grid<T extends View, G extends GridProps<T>>(props: G) {
  function getData(filter: G['filters']): T['Data'] {
    // return data
    return {}
  }
  return (
    <table>
    </table>
  )
}