تایپ اسکریپت – محافظ های نوع اتحادیه از نوع 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; }
زمین بازی تی اس
همین، فکر میکنم از این طریق میتوانید کد را واضحتر، خواناتر و مستندتر کنید.
منابع: