برنامه نویسی

تایپ اسکریپت – محافظ های نوع اتحادیه از نوع e

سلام بچه ها!

امروز می خواهم کمی بیشتر در مورد تایپ اسکریپت به اشتراک بگذارم، به ویژه برخی از تکنیک های استفاده از آن Union Types ه type guards.

من به شما نشان خواهم داد که چگونه می توانید کد تایپ اسکریپ خود را بهبود ببخشید تا آن را خواناتر و به خوبی مستند کنید.

1 – انواع اتحادیه

یکی Union types نوعی است که توسط انواع دیگر تشکیل شده است، جایی که می تواند هر یک از آن مقادیر ممکن را در نظر بگیرد.

چند نمونه

type NumberOrString = number | string;

type Status = "idle" | "loading" | "success" | "failure"

// React useState, can receive a value or a function as parameter to serve as initial value. 
// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a03856975a17eba524739676affbf70ac4078176/types/react/v17/index.d.ts#L920
function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

2 – تایپگارد

در حال حاضر typeguard این راهی برای گفتن تایپ اسکریپت، از طریق برخی جریان کنترل است if/else, loops, ternários e etc، پالایش – ما آن را می نامیم محدود، تنگ – یک نوع خاص به یک نوع خاص تر در محدوده تعریف شده.

به این ترتیب، می توانیم استنباط کنیم که در نقطه ای از کد، یک نوع می تواند نوع خاصی را در نظر بگیرد.

برای کسانی که می خواهند در مورد بیشتر مطالعه کنند narrowing e typeguards، مثال های فراوان و توضیح کامل وجود دارد.

ما می توانیم انجام دهیم narrowing فقط با عملیات جاوا اسکریپت.

// using switch
type Order = OrderCompleted | OrderProcessing | OrderReady;

function processOrder(order: Order) {
  switch (order.status) {
    case "completed": {
      console.log(order.deliveryAt) // Narrow to OrderCompleted 
      return;
    }
    case "processing": {
      console.log(order.expectDeliveryAt) // Narrow to OrderProcessing 
      return;
    }
    case "ready": {
      console.log(order.productId) // Narrow to OrderReady 
    }
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

زمین بازی تی اس

// using in operator
interface Dog {
    walk: () => void;
}

interface Fish {
    swim: () => void;
}

type Animal = Dog | Fish;

function move(animal: Animal) {
    if ("walk" in animal) {
        animal.walk(); // Narrow to Dog
        return;
    }

    animal.swim(); // Narrow to Fish
}


// using typeof
function promise(callback?: () => void) {
    // ...
    if (typeof callback !== "undefined") {
        callback(); // So in this scope you ensure that function exists
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

زمین بازی Ts

تمام کاری که ما تاکنون انجام داده ایم این است که از جریان کد استفاده کنیم تا از عملیات جاوا اسکریپت برای کمک به تایپ اسکریپت برای انجام آنچه می خواهد استفاده کنیم. narrowing و انواع خاص تری را استنباط کنید.

حال، اگر بخواهیم اعتبارسنجی های خودمان را انجام دهیم، بدون استفاده صریح از عملیات جاوا اسکریپت مانند typeof، in، instanceof و دیگران، در جریان کد، اما برای کپسوله کردن تمام منطق اعتبارسنجی در یک تابع، مانند تجزیه کننده یا چیزی. برای آن می توانیم از type predicates تایپ را انجام دهید

3 – محمولات را تایپ کنید

یک مثال بسیار رایج زمانی است که ما با API تماس می گیریم و فقط نوع برگشت را استنباط می کنیم، اما چه چیزی به ما تضمین می کند که API شی درست را ارسال کرده است؟

مشکل

interface User {
    name: string;
    email: string;
}

declare const api: <T>(endPoint: string) => T

function getUser() {
    const user = api<User>("/user"); // We assume that the response payload actually sent the object with the type strictly equal to User

    console.log(user.email); 
    console.log(user.name);
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ما می توانیم یک تابع برای اطمینان از اینکه شی user واقعا نوع است User.

interface User {
    name: string;
    email: string;
}

function isUser(input: unknown): input is User  {
    return (
        typeof input === "object" && 
        !!input &&
        "name" in input &&
        "email" in input
    ) 
}

declare const api: <T>(endPoint: string) => T

function getUser() {
    const user = api("/user"); // We can pass the User as a generic here, however we have to remember to do the validation

    if (isUser(user)) { // With this type guard, we can be sure that the API response actually returned a User. 
        console.log(user.email); 
        console.log(user.name);
        return;
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

زمین بازی Ts

اما اینجا هنوز یک مشکل وجود دارد، به زمین بازی نگاه کنید.

فرض کنید در پایان دوی سرعت هستیم و بعد یک فوریت پیش می آید و باید یک رشته دیگر در نوع معرفی کنیم. Userبه عنوان مثال، زمینه avatar. نقش ما isUser هیچ خطایی را متهم نمی کند زیرا ما در حال بررسی این زمینه نیستیم avatar.

از آنجایی که اسپرینت در حال پایان است و ما با عجله تغییر را انجام دادیم، فراموش کردیم عملکرد و عملکرد خود را تغییر دهیم isUser عملکردی نیست، با آنچه باید مطابقت ندارد، چرا اعتبار نمی دهد اگر در واقع شی یک User. بنابراین، بوم، ما یک اشکال بالقوه را در تولید معرفی کرده ایم.

ما می‌توانیم این مشکل را با تغییر کمی تابع خود برای تجزیه‌کننده بودن در واقع حل کنیم و با اعتبارسنجی نوع هر ویژگی آن را کامل‌تر کنیم.

function parseUser(input: unknown): User | null {
    const isObject = typeof input === "object" && input
    if (!isObject) return null;

    const hasUserProperties = 
        "name" in input &&
        "email" in input

    if (!hasUserProperties) return null;

    const { email, name } = input;

    if (typeof email !== "string") return null;
    if (typeof name !== "string") return null;

    return { email, name } // Property 'avatar' is missing in type '{ email: string; name: string; }' but required in type 'User'.(2741)
}

function isUser(input: unknown): input is User {
    return input !== null
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در زمین بازی ببینید، حالا اگر ویژگی های جدید اضافه شود، عملکرد ما خطا نشان می دهد.

ما قبلاً ابزارهای عالی برای انجام این همه تجزیه داریم، مانند zod

4 – Extract Utility

در نهایت، من می خواهم یک ابزار بسیار قدرتمند را به شما نشان دهم که تایپ اسکریپ هنگام کار با آن ارائه می دهد union types.

به عنوان مثال، اگر union از یک کتابخانه خارجی است، و ما به هر مقدار ممکن به صورت مجزا دسترسی نداریم. ما می توانیم نوع خاص را با استفاده از استنباط کنیم Extract.

O Extract دو ژنریک دریافت می کند، اولی Union در واقع و دوم فقط اتحادیه است که استخراج خواهد شد.

type Payment = 
    | { status: "pending" }
    | { status: "paid", paidAt: string }
    | { status: "canceled", canceledAt: string } 

type PendingPayment = Extract<Payment, { status: "pending" }>; // { status: "pending" }
type PaidPayment = Extract<Payment, { status: "paid" }>; // { status: "paid", paidAt: string }
type CanceledPayment = Extract<Payment, { status: "canceled" }>; // { status: "canceled", canceledAt: string } 

type Shape = {
  kind: "square";
  size: number;
} | {
  kind: "circle";
  radius: number;
};

type Square = Extract<Shape, { status: "square" }>; // { kind: "square"; size: number; } 
type Circle = Extract<Shape, { status: "circle" }>; // { kind: "circle"; radius: number; }
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

زمین بازی تی اس

همین، فکر می‌کنم از این طریق می‌توانید کد را واضح‌تر، خواناتر و مستندتر کنید.

منابع:

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا