Ako zoskupiť rodinu súvisiacich typov v TypeScripte
Takže v mojej aplikácii mám komponent mriežky, ktorý sa používa pre niekoľko rôznych typov údajov.
Všetky typy dát sa získavajú prostredníctvom rôznych API koncových bodov, s použitím rôznych možností filtrovania atď. Všetko je to riadne definované s vhodnými typmi a funguje to dostatočne dobre, keď pracujem s konkrétnym typom.
Ako môžem všetky tieto typy „zoskupiť“ podľa „rodiny“?
Chcel by som byť schopný opísať „tvar“ rodiny (napr. má typ Filtra, typ Odpovede atď.) a byť schopný vynútiť vzťah (napr. keď máme tento dátový typ, potrebujeme tento typ filtra).
Vyskúšal som rôzne prístupy pomocou rozhraní a tried, ale nemôžem špecifikovať typ s "." v názve; to je vyhradené pre menné priestory.
Priestory názvov sú sľubné, ale nemôžem vynútiť ich „tvar“, takže nemôžem mať svoju komponentu používajúcu spoločný „abstraktný“ priestor názvov.
Máte nejakú myšlienku ohľadom prístupu, ktorý by som mal použiť?
UPRAVIŤ: Pre objasnenie, tu je to, čo by som chcel urobiť:
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>
Nie je to 100 % syntakticky správne, ale dúfam, že to ilustruje koncept. V podstate chcem „prepojiť“ typy Task a TaskFilter cez spoločnú štruktúru.
Avšak T.Data, ako je popísané vyššie, nefunguje, pretože T nie je menný priestor ('T' odkazuje iba na typ, ale tu sa používa ako menný priestor).
Odpoveď č. 1
Keď musíte vytvoriť „spoločné“ rozhranie pre objekty, ktoré majú niektoré spoločné polia, zatiaľ čo iné sú odlišné, môžete použiť zápis | a definovať nový type. Pozrite si príklad nižšie:
//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
}
Upozornenie: keď vytvárate objekt, musíte manuálne zadať parameter type.
Upozornenie 2: tento prístup zvyčajne nefunguje dobre (z hľadiska udržiavateľnosti), keď potrebujete viacero stupňov zoskupovania. Budete musieť definovať viacero polí type pre každú skupinu a môže sa to veľmi rýchlo stať chaotickým.
AKTUALIZÁCIA Toto by mala byť implementácia toho, čo ste požadovali, pomocou vyššie uvedenej metódy
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>
)
}
