Vyhledávání na webu

Jak seskupit rodinu příbuzných typů v TypeScriptu


Takže v mé aplikaci mám mřížkovou komponentu, která se používá pro několik různých typů dat.

Všechny typy dat jsou získávány prostřednictvím různých API endpointů, s využitím různých možností filtrování atd. Vše je přitom správně definováno s vhodnými typy a funguje to dostatečně dobře, když pracuji s konkrétním typem.

Jak mohu všechny tyto typy „seskupit“ podle „rodiny“?

Chtěl bych být schopen popsat „tvar“ rodiny (např. má typ Filtru, typ Odezvy atd.) a být schopen vynutit vztah (např. když máme tento datový typ, potřebujeme tento typ filtru).

Zkoušel jsem různé přístupy s použitím rozhraní a tříd, ale nemohu specifikovat typ s "." v něm; to je vyhrazeno pro jmenné prostory.

Jmenné prostory jsou slibné, ale nemohu vynutit jejich „tvar“, takže nemohu mít svou komponentu používající společný „abstraktní“ jmenný prostor.

Máte nějakou myšlenku na přístup, který bych měl použít?

UPRAVA: Pro upřesnění, tady je to, co bych chtěl udělat:

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>

Není to 100% syntakticky správné, ale doufám, že to ilustruje koncept. V podstatě chci „propojit“ typy Task a TaskFilter přes společnou strukturu.

Nicméně T.Data, jak je popsáno výše, nefunguje, protože T není jmenný prostor ('T' odkazuje pouze na typ, ale zde je používán jako jmenný prostor).

Odpověď č. 1

Když musíte vytvořit „společné“ rozhraní pro objekty, které mají některá společná pole, zatímco jiná jsou odlišná, můžete použít notaci | a definovat nový typ. Podívejte se na následující příklad:

//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
}

Upozornění: při vytváření objektu musíte ručně zadat parametr type.

Upozornění 2: Tento přístup obvykle nefunguje dobře (z hlediska udržovatelnosti), když potřebujete více úrovní seskupení. Budete muset definovat více polí type pro každou skupinu a může to velmi rychle začít být chaotické.

AKTUALIZACE Toto by měla být implementace toho, co jste požadovali, pomocí výše uvedené metody

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>
  )
}