Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds

Komponenty Generyczne - Uniwersalne Systemy Nawigacji

Wyobraź sobie uniwersalny system nawigacji na statku kosmicznym, który może wyswietlac dane o planetach, gwiazdach, statkach - cokolwiek mu podasz. W TypeScript takie "uniwersalne" komponenty tworzymy dzięki generyk.

Czym sa generyki?

Generyki to parametry typow - pozwalają tworzyć komponenty i funkcje, które działają z różnymi typami danych, zachowujac pelna kontrole typow:

1// Bez generykow - trzeba pisac osobne funkcje
2function getFirstPlanet(items: Planet[]): Planet {
3  return items[0];
4}
5function getFirstStar(items: Star[]): Star {
6  return items[0];
7}
8
9// Z generykami - jedna funkcja dla wszystkich typow
10function getFirst<T>(items: T[]): T {
11  return items[0];
12}
13
14const planet = getFirst<Planet>(planets);  // zwraca Planet
15const star = getFirst<Star>(stars);        // zwraca Star
16const name = getFirst<string>(["a", "b"]); // zwraca string

Generyczny komponent listy

Jednym z najczestszych zastosowan generykow w React jest uniwersalny komponent listy:

1// Kazdy element musi miec id i label do wyświetlenia
2interface HasId {
3  id: number | string;
4}
5
6interface ListProps<T extends HasId> {
7  items: T[];
8  renderItem: (item: T) => React.ReactNode;
9  emptyMessage?: string;
10}
11
12function List<T extends HasId>({ items, renderItem, emptyMessage = "Brak danych" }: ListProps<T>) {
13  if (items.length === 0) {
14    return <p>{emptyMessage}</p>;
15  }
16
17  return (
18    <ul>
19      {items.map(item => (
20        <li key={item.id}>{renderItem(item)}</li>
21      ))}
22    </ul>
23  );
24}

Uzycie generycznego komponentu

1interface Planet {
2  id: number;
3  name: string;
4  type: string;
5}
6
7interface CrewMember {
8  id: string;
9  name: string;
10  rank: string;
11}
12
13function Dashboard() {
14  const planets: Planet[] = [
15    { id: 1, name: "Mars", type: "skalista" },
16    { id: 2, name: "Jupiter", type: "gazowy" }
17  ];
18
19  const crew: CrewMember[] = [
20    { id: "c1", name: "Nova", rank: "Kapitan" },
21    { id: "c2", name: "Ra", rank: "Nawigator" }
22  ];
23
24  return (
25    <div>
26      <h2>Planety</h2>
27      <List<Planet>
28        items={planets}
29        renderItem={(planet) => <span>{planet.name} ({planet.type})</span>}
30      />
31
32      <h2>Zaloga</h2>
33      <List<CrewMember>
34        items={crew}
35        renderItem={(member) => <span>{member.name} - {member.rank}</span>}
36      />
37    </div>
38  );
39}

Generyczny komponent Select

1interface SelectOption {
2  value: string;
3  label: string;
4}
5
6interface SelectProps<T extends SelectOption> {
7  options: T[];
8  value: string;
9  onChange: (option: T) => void;
10  placeholder?: string;
11}
12
13function Select<T extends SelectOption>({
14  options, value, onChange, placeholder
15}: SelectProps<T>) {
16  const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
17    const selected = options.find(opt => opt.value === e.target.value);
18    if (selected) onChange(selected);
19  };
20
21  return (
22    <select value={value} onChange={handleChange}>
23      {placeholder && <option value="">{placeholder}</option>}
24      {options.map(opt => (
25        <option key={opt.value} value={opt.value}>
26          {opt.label}
27        </option>
28      ))}
29    </select>
30  );
31}

Ograniczenia generykow (constraints)

Slowo kluczowe

extends
pozwala ograniczyc, jakie typy sa akceptowane:

1// T musi miec pole 'name'
2function displayName<T extends { name: string }>(item: T): string {
3  return item.name;
4}
5
6// T musi implementowac interfejs Sortable
7interface Sortable {
8  sortOrder: number;
9}
10
11function sortItems<T extends Sortable>(items: T[]): T[] {
12  return [...items].sort((a, b) => a.sortOrder - b.sortOrder);
13}
Vai a CodeWorlds