🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

أنماط تفاعل TypeScript 2026: المكونات العامة والمركب والخطافات

⏱️5 min read  ·  985 words

تتجاوز أنماط تفاعل TypeScript في عام 2026 كتابة المكونات الأساسية. تستفيد المكونات المركبة، وخصائص العرض، والخطافات المخصصة ذات أنواع الإرجاع المعقدة، وأنماط مكونات الخادم الجديدة في React 19، كلها من نظام الكتابة الخاص بـ TypeScript. يغطي هذا الدليل الأنماط التي يستخدمها كبار مطوري React يوميًا.

مكونات عامة

// Generic list component — renders any data type
interface ListProps<T> {
  items: T[];
  renderItem: (item: T, index: number) => React.ReactNode;
  keyExtractor: (item: T) => string | number;
  emptyComponent?: React.ReactNode;
  className?: string;
}

function List<T>({
  items,
  renderItem,
  keyExtractor,
  emptyComponent = <p>No items</p>,
  className,
}: ListProps<T>) {
  if (items.length === 0) return <>{emptyComponent}</>;

  return (
    <ul className={className}>
      {items.map((item, index) => (
        <li key={keyExtractor(item)}>
          {renderItem(item, index)}
        </li>
      ))}
    </ul>
  );
}

// Usage — fully type-safe
interface User { id: number; name: string; email: string; }

<List<User>
  items={users}
  keyExtractor={u => u.id}
  renderItem={user => (
    <UserCard user={user} />
  )}
/>

مكونات متعددة الأشكال

// A component that can render as any HTML element or component
type AsProp<T extends React.ElementType> = { as?: T };
type PropsToOmit<T extends React.ElementType, P> = keyof (AsProp<T> & P);

type PolymorphicComponentProp<T extends React.ElementType, Props = {}> = React.PropsWithChildren<
  Props & AsProp<T>
> &
  Omit<React.ComponentPropsWithoutRef<T>, PropsToOmit<T, Props>>;

type PolymorphicRef<T extends React.ElementType> = React.ComponentPropsWithRef<T>["ref"];

type PolymorphicComponentPropWithRef<T extends React.ElementType, Props = {}> =
  PolymorphicComponentProp<T, Props> & { ref?: PolymorphicRef<T> };

// Text component that renders as any element
interface TextProps {
  variant?: "h1" | "h2" | "h3" | "body" | "caption";
  color?: "primary" | "secondary" | "muted";
}

const variantMap = { h1: "h1", h2: "h2", h3: "h3", body: "p", caption: "span" } as const;

function Text<T extends React.ElementType = "span">({
  as,
  variant = "body",
  color = "primary",
  children,
  className,
  ...props
}: PolymorphicComponentProp<T, TextProps>) {
  const Component = as ?? variantMap[variant];
  return (
    <Component
      className={`text-${variant} text-${color} ${className ?? ""}`}
      {...props}
    >
      {children}
    </Component>
  );
}

// Usage
<Text variant="h1">Heading 1</Text>                 // renders <h1>
<Text as="label" htmlFor="email">Email</Text>        // renders <label>
<Text as={Link} to="/about" variant="body">About</Text>  // renders <Link>

نمط المكونات المركبة

import { createContext, useContext, useState } from 'react';

// Accordion compound component
interface AccordionContextValue {
  openItem: string | null;
  setOpenItem: (id: string | null) => void;
}

const AccordionContext = createContext<AccordionContextValue | null>(null);

function useAccordion() {
  const ctx = useContext(AccordionContext);
  if (!ctx) throw new Error('Must be used within <Accordion>');
  return ctx;
}

function Accordion({ children, defaultOpen = null }: {
  children: React.ReactNode;
  defaultOpen?: string | null;
}) {
  const [openItem, setOpenItem] = useState<string | null>(defaultOpen);
  return (
    <AccordionContext.Provider value={{ openItem, setOpenItem }}>
      <div className="accordion">{children}</div>
    </AccordionContext.Provider>
  );
}

function AccordionItem({ id, title, children }: {
  id: string;
  title: string;
  children: React.ReactNode;
}) {
  const { openItem, setOpenItem } = useAccordion();
  const isOpen = openItem === id;

  return (
    <div className={`accordion-item ${isOpen ? 'open' : ''}`}>
      <button onClick={() => setOpenItem(isOpen ? null : id)}>
        {title}
        <span>{isOpen ? '▲' : '▼'}</span>
      </button>
      {isOpen && <div className="accordion-content">{children}</div>}
    </div>
  );
}

// Attach as namespaced components
Accordion.Item = AccordionItem;

// Usage
<Accordion defaultOpen="faq-1">
  <Accordion.Item id="faq-1" title="What is TypeScript?">
    TypeScript is a typed superset of JavaScript...
  </Accordion.Item>
  <Accordion.Item id="faq-2" title="Why use React?">
    React provides a declarative way to build UIs...
  </Accordion.Item>
</Accordion>

الخطافات المخصصة المكتوبة

import { useCallback, useReducer } from 'react';

// Async state management hook
type AsyncState<T> =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: T }
  | { status: 'error'; error: Error };

type AsyncAction<T> =
  | { type: 'start' }
  | { type: 'success'; data: T }
  | { type: 'error'; error: Error }
  | { type: 'reset' };

function asyncReducer<T>(state: AsyncState<T>, action: AsyncAction<T>): AsyncState<T> {
  switch (action.type) {
    case 'start': return { status: 'loading' };
    case 'success': return { status: 'success', data: action.data };
    case 'error': return { status: 'error', error: action.error };
    case 'reset': return { status: 'idle' };
  }
}

function useAsync<T, Args extends unknown[]>(
  fn: (...args: Args) => Promise<T>
) {
  const [state, dispatch] = useReducer(
    asyncReducer as React.Reducer<AsyncState<T>, AsyncAction<T>>,
    { status: 'idle' } as AsyncState<T>
  );

  const execute = useCallback(
    async (...args: Args) => {
      dispatch({ type: 'start' });
      try {
        const data = await fn(...args);
        dispatch({ type: 'success', data });
        return data;
      } catch (err) {
        const error = err instanceof Error ? err : new Error(String(err));
        dispatch({ type: 'error', error });
        throw error;
      }
    },
    [fn]
  );

  const reset = useCallback(() => dispatch({ type: 'reset' }), []);

  return { ...state, execute, reset };
}

// Usage with full type inference
function UserProfile({ userId }: { userId: number }) {
  const { status, data, error, execute } = useAsync(fetchUser);

  useEffect(() => { execute(userId); }, [userId]);

  if (status === 'loading') return <Spinner />;
  if (status === 'error') return <Error message={error.message} />;
  if (status === 'success') return <Profile user={data} />;  // data is User
  return null;
}

رد فعل 19 مكونات الخادم مع TypeScript

// Server Component — no 'use client', runs on server
// app/blog/[slug]/page.tsx
import type { Metadata } from 'next';

interface Props {
  params: Promise<{ slug: string }>;
  searchParams: Promise<{ view?: string }>;
}

// Type-safe metadata generation
export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const { slug } = await params;
  const post = await fetchPost(slug);

  return {
    title: post.title,
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      images: [{ url: post.coverImage }],
    },
  };
}

export default async function PostPage({ params, searchParams }: Props) {
  const { slug } = await params;
  const { view = 'full' } = await searchParams;

  const post = await fetchPost(slug);
  if (!post) notFound();

  return (
    <article>
      <h1>{post.title}</h1>
      {view === 'full' ? (
        <div dangerouslySetInnerHTML={{ __html: post.contentHtml }} />
      ) : (
        <p>{post.excerpt}</p>
      )}
      <Suspense fallback={<CommentSkeleton />}>
        <Comments postId={post.id} />
      </Suspense>
    </article>
  );
}

// Client component with typed props
'use client';

interface CommentFormProps {
  postId: number;
  onSubmit: (comment: { text: string; author: string }) => Promise<void>;
}

function CommentForm({ postId, onSubmit }: CommentFormProps) {
  // ...client-side interactivity
}

تعد أنماط TypeScript React في عام 2026 ناضجة – حيث تعمل المكونات العامة على التخلص من التعليمات البرمجية المكررة، وتقوم المكونات متعددة الأشكال بإنشاء واجهات برمجة تطبيقات مرنة، وتحل المكونات المركبة محل الحفر المعقد للدعائم، والخطافات المخصصة المكتوبة تجعل الأنماط غير المتزامنة آمنة. يؤدي الجمع بين مكونات خادم React 19 مع TypeScript إلى إنشاء تطبيقات كاملة آمنة للنوع.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇬🇧 English🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা