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