برنامه نویسی

عدم نامگذاری انواع و تبدیل نوع در انگولار توسط TypeScript

Summarize this content to 400 words in Persian Lang
افکار تصادفی در TypeScript. من متوجه 2 چیز در پروژه Angular بزرگتر شده ام:

نامگذاری انواع (با نام مستعار) معمول نیست
تبدیل نوع یا اتفاق نمی افتد، یا با ایمنی زیاد انجام نمی شود

عدم نامگذاری / نامگذاری انواع

2 سالی که در زبان‌های تایپ شده خوب گذرانده‌ام، و 1 سالی که در BFF سپری کرده‌ام، با استفاده از Zod، نام‌گذاری انواع شما و قرار دادن آنها در کنار کدی که از آنها استفاده می‌کند، برای من فوق‌العاده عادی شده است.

واکنش نشان دهید؟ شما نوع را برای props نام می‌برید، سپس از آن در props استفاده می‌کنید:

type UserProfileProps = { name: string, age: number }
const UserProfile = (props:UserProfileProps) =>

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

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

JSON؟ شما یک طرحواره Zod ایجاد می کنید و یک نوع به همین نام دارید:

cons User = z.object({
name: z.string(),
age: z.number()
})
type User = z.infer<typeof User>

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

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

تابع می تواند موفق یا شکست بخورد، اما شما از نوع بازگشت مطمئن نیستید، بنابراین از یک عمومی استفاده می کنید؟

type Result<T> = Ok<T> | Err
type Ok = { type: ‘ok’, data: T }
type Err = { type: ‘err’, error: string }

const parseUser = (json:any):Result<User> => {
const result = User.safeParse(json)
if(result.success === false) {
return Err(result.error)
} else {
return Ok(result.data)
}
}

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

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

در هر 3 مورد، چند الگو را مشاهده خواهید کرد:

نوع یک نام صریح دارد
نوع مورد استفاده قرار می گیرد (مثلاً “نزدیک”).

چیزی که من متوجه شده ام این است که اکثر توسعه دهندگانی که با آنها کار می کنم انواع خود را نام نمی برند. افراد زیادی هستند که Enum های خود را نامگذاری می کنند، به ویژه آنهایی که برای ثبت رشته های جادویی متعددی که توسعه دهندگان رابط کاربری با آنها سر و کار داریم استفاده می شوند… اما آنها در ماژول های “Model” قرار می گیرند، به نظر من بازگشتی به MVC (معروف به Model View Controller) است. روزهایی که مدل «داده» شماست، حتی اگر Enum هیچ داده ای را در خود نگه نمی دارد، اما صرفاً تعاریفی برای دریافت داده است.

برای من این … عجیب و ناخوشایند است و به نظر می رسد DRY (تکرار نکنید) برای انواع اعمال نمی شود، بلکه فقط برای متغیرها/توابع/کلاس ها اعمال می شود. که به نوبه خود باعث می شود من بروم “waaaaaat!؟”.

Elm، ReScript، F#، Ocaml، Scala… طبیعی است که انواع خود را نام ببرید، سپس از آنها برای مکان استفاده کنید. در واقع، شما اغلب انواع _قبل از کد را ایجاد می کنید، حتی اگر واقعاً DDD (طراحی مبتنی بر دامنه) را تمرین نکنید. بله، هنگام انجام توابع کارهای زیادی را انجام خواهید داد، یا شروع به آزمایش چیزها می کنید و تصمیم می گیرید طراحی خود را تغییر دهید و انواع جدیدی بسازید. در هر صورت، این فقط “هنجار” است. سپس هنجارهای دیگر مانند “نام تابع” و “نام متغیرهای خود” را انجام می دهید. من کمی گیج هستم که چرا در این پروژه TypeScript Angular فقط 2 از 3 (متغیرها و توابع، نه انواع) وجود دارد. باید سایر پروژه های داخلی Angular را بررسی کنم و ببینم آیا در آنجا نیز رایج است یا خیر.

عجیب است، به یاد دارم که Elm و ReScript DO هر دو قابلیت نامگذاری انواع شما را دارند. لایک به جای کاربر مانند بالا، هر دو پشتیبانی می کنند:

getUser : { name:string, age: number }

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

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

متوجه خواهید شد که اینطور نیست getUser: User. شما فقط می توانید اشیاء خود را در Elm و ReScript درست مانند TypeScript ایجاد کنید. این انواع ناشناس را در همه جا خواهید دید:

getUser : { name:string, age: number }

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

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

به جای:

type NameOrNothing = string | undefined
loadSomeData():NameOrNothing

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

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

مورد علاقه من انواع ضبط بسیار خوب فکر شده است که 7 بار در یک فایل استفاده می شود. برای من این میوه بسیار آسان و بسیار کم آویزان خشک کردن است… و در عین حال….

getData():FormData<Record<FormInput, DataResult>>
processForm(record:FormData<Record<FormInput, DataResult>>)
logAlternative(record:FormData<Record<FormInput, DataResult>>):Observable<FormData<Record<FormInput, DataResult>>>

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

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

… به جای:

type FormRecord = FormData<Record<FormInput, DataResult>>

getData():FormRecord
processForm(record:FormRecord)
logAlternative(record:FormRecord):Observable<FormRecord>

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

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

این نیز به طور تصادفی وسواس نوع بدوی را تشویق می کند، اما خوشبختانه سن به من کمک کرد تا در حال حاضر از گریه کردن در مورد آن در روابط عمومی خودداری کنم.

تبدیل نوع ایمن کم به بدون

این یکی در چند ماه گذشته واقعاً باعث نگرانی من در مورد انواع روابط عمومی شده است. جاهایی وجود دارد که ما باید تایپ تایپ اسکریپت را باریک کنیم… و یا این کار را نمی کنیم یا به اندازه کافی محدود نمی کنیم. برای کسانی از شما که از تایپ اسکریپت یا زبان‌هایی که به تدریج تایپ می‌شوند نیستید، وقتی می‌گوییم «محدود کردن تایپ»، منظور ما «از انواع استفاده می‌کنیم» برای محدود کردن فضای مشکلمان نیست. منظور ما باریک کردن در زمینه درست کردن انواع _more_ است. برای کسانی از شما که به زبان های تایپ شده ای که یاد گرفته اید از وسواس بدوی دوری کنید، اینطور است.

TypeScript یکی از آن زبان هایی است که به تدریج تایپ می شود، یعنی می توانید سطح تایپ را کم یا زیاد کنید. اگر نمی دانید یا نمی دانید که چیزی می تواند هر چیزی باشد، از نوعی به نام “هر” یا “ناشناس” استفاده می کنید. اگر می دانید چیزی همیشه یک رشته است، می توانید از نوع “رشته” استفاده کنید. با این حال، چگونه چیزی را از “هر” به “رشته” تبدیل می کنید؟ اگر _از قبل_ رشته ای باشد چه؟ این همان چیزی است که TypeScript آن را باریک کردن نوع می نامد. حصول اطمینان از انواعی که می گیرید که باریک نیستند و گسترده در نظر گرفته می شوند (الا انواع بسیار متنوعی می تواند باشد)، به یک نوع باریک (فقط 1 یا 2 نوع احتمالاً _است).

بسیاری از این نوع تکنیک‌های باریک‌سازی شبیه اظهارات/بررسی‌های زمان اجرا هستند و برخی نیز چنین هستند. برخی نیستند. هر دو به TypeScript کمک می کنند و شما توسعه دهنده اطمینان حاصل کنید که انواع مطابقت دارند.

با این حال، آنچه واقعاً در مورد آن صحبت نشده است، تبدیل انواع است. بسیاری از این موارد، حداقل در اسناد TypeScript، تحت دامنه محدود کردن نوع در نظر گرفته می شوند، اما نه همه آنها. برخی از آن جاوا اسکریپت اولیه است.

برای مثال، تبدیل یک «هر» به «رشته»، می‌تواند به یکی از 3 روش انجام شود: ادعای نوع، محافظ نوع، یا محمول نوع. ادعا، با استفاده از as کلمه کلیدی، خطرناک ترین است. این شما، توسعه دهنده، هستید که به TypeScript می گویید که چیست، و می تواند 100% به شما اعتماد کند. بررسی نوع دیگری انجام نشده است. به طور موثر بررسی تایپ را نه OFF، بلکه “باور داشتن آنچه شما می گویید” خاموش کنید. بدیهی است، کسی که لذت سیستم‌های تایپ شده و اسمی را تجربه کرده است (برخلاف TypeScript که سخت‌گیرانه است، به تدریج تایپ می‌شود و ساختاری تایپ می‌شود) این را وحشتناک می‌بیند:

const name = json as string

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

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

اکنون، کسانی که از سیستم‌های نوع ساختاری، مانند Java/C#/Go استفاده می‌کنید، ممکن است برخی از این موارد ظریف باشند و آنطور که من ادعا می‌کنم سیاه و سفید نباشند. “اگر راه می رود و مانند اردک صحبت می کند … اردک است”. موارد زیادی وجود دارد که توسعه دهنده، با استفاده از as، صحیح است. گاهی اوقات آنها حتی “به اندازه کافی صحیح” هستند.

مورد دوم، بهتر است، حداقل برای افراد اولیه، یک محافظ نوع است، مانند:

if(typeof json === ‘string’) {
console.log(“it’s a string”)
} else {
console.log(“it’s not a string”)
}

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

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

ترکیب این موارد با هم به شما و TypeScript اطمینان بیشتری می دهد که نوع آن چیزی است که شما واقعاً فکر می کنید. در اینجا میدان های مین زیادی وجود دارد (مثلا typeof [] از این رو “شی” است، نه آرایه Array.isArray موجود)، اما دنیاها بهتر از “من بهتر از کامپایلر می دانم” است.

در نهایت، محمولات نوع وجود دارد. توابعی که true یا false را برمی گرداند. با این حال، این محمولات یک نوع بازگشتی منحصر به فرد دارند. یک ادعای نوع:

function isString(json:any):json is string {
return typeof json === ‘string’
}

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

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

توجه کنید که این رشته “is” است، نه رشته “نمونه”، که همچنین یک میدان مین است. نکته مهم این است که می توانید تعداد زیادی محافظ تایپ را در این توابع قرار دهید تا به TypeScript کمک زیادی کند.

پس چرا وقتی در مورد تبدیل انواع در TypeScript صحبت می کنیم، این 3 تکنیک باریک کردن نوع (بیشتر وجود دارد) اهمیت دارند؟ خوب، احتمالاً می‌توانید ببینید که چگونه می‌توانید با خیال راحت هر یک را به رشته تبدیل کنید… اما در مورد هر یک به یک کاربر بدون چیزی مانند Zod چطور؟

const json:any = ???
// convert the above to this
type User = {
name: string,
age: number
}

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

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

شما باید از باریک کردن نوع استفاده کنید تا ابتدا مطمئن شوید که ساختار مورد نیاز شما مطابقت دارد، سپس می‌توانید در صورت امکان با فیلدهای دیگر کد شما را خراب نمی‌کنند (مثلاً Object.keyهایی که فقط 2 فیلد را انتظار دارند، نه بیشتر)، یا آن را با دست جمع کنید، مانند:

if(typeof json === ‘object’
&& typeof json.name === ‘string’
&& typeof json.age === ‘number’
&& isNaN(json.age) === false
… // and even more
return { name: json.name, age: json.age }

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

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

به چند دلیل سخت است:

این مقدار زیادی کد است
این دانش زیادی از جاوا اسکریپت های ابتدایی سطح پایین است
اشکال زدایی این امر مستلزم افزودن آنها به صورت 1 در یک زمان و منتظر ماندن برای سرور زبان TypeScript و بررسی خطاهای کامپایلر برای تنظیم است.
با توجه به میزان کاری که باید انجام دهند، برنامه‌نویس‌ها بلافاصله شروع به مشاهده بازگشت سرمایه (ROI) در تایپ سخت‌گیرانه نمی‌کنند تا به یک سطح اطمینان دقیق و نه صدادار برسند.

… شماره 4 من فکر می کنم قاتل است. من نمی توانم این را تأیید کنم، فقط یک حدس است.

دلایل دیگر ممکن است توسعه دهندگان انواع را به طور ایمن تبدیل نکنند

چیزهای مختلفی وجود دارد که در مبانی کد بزرگتر نیز به این مشکل کمک می کند.

بدون مدل سازی دامنه

اولین مورد عدم مدل‌سازی دامنه از منابع شخص ثالث است، چیزی که ما توسعه دهندگان UI با آن بسیار سروکار دارند و زمانی بدتر می‌شود که خودمان BFF خود را نمی‌نویسیم. وقتی با داده های شخص ثالث مانند JSON از تماس های REST یا Objects در LocalStorage سروکار دارند، برنامه نویس ها به طور پیش فرض به انواع اولیه یا کمتر محدود شده هستند. آنها از رشته یا استفاده خواهند کرد any یا Record. این مسئولیت را بر دوش هر کسی می گذارد که از آن انواع برای محدود کردن بیشتر استفاده می کند.

“ببین مرد، من JSON را تجزیه کردم… شما موفقیت در Observable را دوست ندارید، این مشکل من نیست”.

“چرا از تیم API نپرسید که با چه JSON پاسخ می دهند؟”

“آنها نمی دانند، تیم دارای انتساب است، و من حتی فکر نمی کنم کدی وجود داشته باشد که قابل درک باشد که با آن JSON پاسخ دهد… من فکر می کنم که پیکربندی شده است، بنابراین دیدن سریع آن سخت است.”

“اوه… من…”

توسعه نوع محور چیست؟

دلیل دوم این است که هزاران ادبیات و بحث در مورد توسعه تست محور وجود دارد، اما نه توسعه نوع محور. من سال‌ها در مورد Elixir، Haskell، OCaml، F# و Scala می‌دانستم، و اولین بار زمانی که Elm را یاد گرفتم، نام Type Driven Development را شنیدم. با داشتن این دانش، متوجه شدم که سایر زبان‌های تایپ شده، حتی توسعه‌دهنده‌های Rust، همان کاری را که توسعه‌دهندگان Haskell، OCaml، F# و Scala انجام می‌دهند، انجام می‌دهند: اغلب ابتدا انواع آن‌ها را تعریف می‌کردند، حتی قبل از آزمایش‌ها گاهی اوقات. آنها فقط “آن را انجام دادند” و نامی برای آن نداشتند.

بسیاری از توسعه دهندگان در مورد چیزهایی مانند “غیرممکن کردن موقعیت های غیرممکن از طریق انواع” یا “طراحی مبتنی بر دامنه بدون کلاس” یا “جلوگیری از وسواس اولیه” یا استفاده از انواع برای نفی تعداد واحد تست هایی که باید بنویسید و فعال کردن توانایی نوشتن انواع دیگر تست‌ها مانند تست‌های ویژگی/فازی.

ژنریک ها محبوب تر از صادرات انواع باریک هستند

دلیل سوم این است که بسیاری از چارچوب‌ها و کتابخانه‌ها در چند سال گذشته، علیرغم افزایش عظیم TypeScript، به نظر می‌رسد که انواع محدود را نادیده می‌گیرند. در واقع، بسیاری به دلایل واضح به شدت از ژنریک استفاده کرده‌اند: به توسعه‌دهندگان اجازه می‌دهند که توسط طراحی‌های خود محدود نشوند، بنابراین استفاده از کتابخانه/چارچوب آسان‌تر است. این به فرزندخواندگی کمک می کند، اما به آموزش نوع کمک نمی کند.

برخی حتی در جهت مخالف می روند. در مورد Redux، با وجود تبدیل Redux به Elm به جاوا اسکریپت، و بعداً به TypeScript، هیچ نوع اتحادیه ای یافت نمی شود. تمام هدف اتحادیه ها این است که اطمینان حاصل کنید که Reducers فقط می توانید “1 از 3 کار” را انجام دهید. در عوض، ثابت های رشته یا _شاید_ اگر خوش شانس باشید، یک Enum را خواهید دید. اغلب خطرناک است که از رکوردها برای محدود کردن دامنه استفاده می شود، در حالی که به جای آن باید از اتحادیه استفاده می شد.

نتیجه گیری

من مطمئن نیستم که در مورد عدم نامگذاری انواع غیر از مثال زدن، و قرار دادن انواع در جایی که آنها استفاده می شوند، چه کاری می توان انجام داد.

برای تبدیل نوع، این آسان است: از Zod استفاده کنید.

افکار تصادفی در TypeScript. من متوجه 2 چیز در پروژه Angular بزرگتر شده ام:

  1. نامگذاری انواع (با نام مستعار) معمول نیست
  2. تبدیل نوع یا اتفاق نمی افتد، یا با ایمنی زیاد انجام نمی شود

عدم نامگذاری / نامگذاری انواع

2 سالی که در زبان‌های تایپ شده خوب گذرانده‌ام، و 1 سالی که در BFF سپری کرده‌ام، با استفاده از Zod، نام‌گذاری انواع شما و قرار دادن آنها در کنار کدی که از آنها استفاده می‌کند، برای من فوق‌العاده عادی شده است.

واکنش نشان دهید؟ شما نوع را برای props نام می‌برید، سپس از آن در props استفاده می‌کنید:

type UserProfileProps = { name: string, age: number }
const UserProfile = (props:UserProfileProps) =>
وارد حالت تمام صفحه شوید

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

JSON؟ شما یک طرحواره Zod ایجاد می کنید و یک نوع به همین نام دارید:

cons User = z.object({
  name: z.string(),
  age: z.number()
})
type User = z.infer<typeof User>
وارد حالت تمام صفحه شوید

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

تابع می تواند موفق یا شکست بخورد، اما شما از نوع بازگشت مطمئن نیستید، بنابراین از یک عمومی استفاده می کنید؟

type Result<T> = Ok<T> | Err
type Ok = { type: 'ok', data: T }
type Err = { type: 'err', error: string }

const parseUser = (json:any):Result<User> => {
  const result = User.safeParse(json)
  if(result.success === false) {
    return Err(result.error)
  } else {
    return Ok(result.data)
  }
}
وارد حالت تمام صفحه شوید

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

در هر 3 مورد، چند الگو را مشاهده خواهید کرد:

  • نوع یک نام صریح دارد
  • نوع مورد استفاده قرار می گیرد (مثلاً “نزدیک”).

چیزی که من متوجه شده ام این است که اکثر توسعه دهندگانی که با آنها کار می کنم انواع خود را نام نمی برند. افراد زیادی هستند که Enum های خود را نامگذاری می کنند، به ویژه آنهایی که برای ثبت رشته های جادویی متعددی که توسعه دهندگان رابط کاربری با آنها سر و کار داریم استفاده می شوند… اما آنها در ماژول های “Model” قرار می گیرند، به نظر من بازگشتی به MVC (معروف به Model View Controller) است. روزهایی که مدل «داده» شماست، حتی اگر Enum هیچ داده ای را در خود نگه نمی دارد، اما صرفاً تعاریفی برای دریافت داده است.

برای من این … عجیب و ناخوشایند است و به نظر می رسد DRY (تکرار نکنید) برای انواع اعمال نمی شود، بلکه فقط برای متغیرها/توابع/کلاس ها اعمال می شود. که به نوبه خود باعث می شود من بروم “waaaaaat!؟”.

Elm، ReScript، F#، Ocaml، Scala… طبیعی است که انواع خود را نام ببرید، سپس از آنها برای مکان استفاده کنید. در واقع، شما اغلب انواع _قبل از کد را ایجاد می کنید، حتی اگر واقعاً DDD (طراحی مبتنی بر دامنه) را تمرین نکنید. بله، هنگام انجام توابع کارهای زیادی را انجام خواهید داد، یا شروع به آزمایش چیزها می کنید و تصمیم می گیرید طراحی خود را تغییر دهید و انواع جدیدی بسازید. در هر صورت، این فقط “هنجار” است. سپس هنجارهای دیگر مانند “نام تابع” و “نام متغیرهای خود” را انجام می دهید. من کمی گیج هستم که چرا در این پروژه TypeScript Angular فقط 2 از 3 (متغیرها و توابع، نه انواع) وجود دارد. باید سایر پروژه های داخلی Angular را بررسی کنم و ببینم آیا در آنجا نیز رایج است یا خیر.

عجیب است، به یاد دارم که Elm و ReScript DO هر دو قابلیت نامگذاری انواع شما را دارند. لایک به جای کاربر مانند بالا، هر دو پشتیبانی می کنند:

getUser : { name:string, age: number }
وارد حالت تمام صفحه شوید

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

متوجه خواهید شد که اینطور نیست getUser: User. شما فقط می توانید اشیاء خود را در Elm و ReScript درست مانند TypeScript ایجاد کنید. این انواع ناشناس را در همه جا خواهید دید:

getUser : { name:string, age: number }
وارد حالت تمام صفحه شوید

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

به جای:

type NameOrNothing = string | undefined
loadSomeData():NameOrNothing
وارد حالت تمام صفحه شوید

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

مورد علاقه من انواع ضبط بسیار خوب فکر شده است که 7 بار در یک فایل استفاده می شود. برای من این میوه بسیار آسان و بسیار کم آویزان خشک کردن است… و در عین حال….

getData():FormData<Record<FormInput, DataResult>>
processForm(record:FormData<Record<FormInput, DataResult>>)
logAlternative(record:FormData<Record<FormInput, DataResult>>):Observable<FormData<Record<FormInput, DataResult>>>
وارد حالت تمام صفحه شوید

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

… به جای:

type FormRecord = FormData<Record<FormInput, DataResult>>

getData():FormRecord
processForm(record:FormRecord)
logAlternative(record:FormRecord):Observable<FormRecord>
وارد حالت تمام صفحه شوید

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

این نیز به طور تصادفی وسواس نوع بدوی را تشویق می کند، اما خوشبختانه سن به من کمک کرد تا در حال حاضر از گریه کردن در مورد آن در روابط عمومی خودداری کنم.

تبدیل نوع ایمن کم به بدون

این یکی در چند ماه گذشته واقعاً باعث نگرانی من در مورد انواع روابط عمومی شده است. جاهایی وجود دارد که ما باید تایپ تایپ اسکریپت را باریک کنیم… و یا این کار را نمی کنیم یا به اندازه کافی محدود نمی کنیم. برای کسانی از شما که از تایپ اسکریپت یا زبان‌هایی که به تدریج تایپ می‌شوند نیستید، وقتی می‌گوییم «محدود کردن تایپ»، منظور ما «از انواع استفاده می‌کنیم» برای محدود کردن فضای مشکلمان نیست. منظور ما باریک کردن در زمینه درست کردن انواع _more_ است. برای کسانی از شما که به زبان های تایپ شده ای که یاد گرفته اید از وسواس بدوی دوری کنید، اینطور است.

TypeScript یکی از آن زبان هایی است که به تدریج تایپ می شود، یعنی می توانید سطح تایپ را کم یا زیاد کنید. اگر نمی دانید یا نمی دانید که چیزی می تواند هر چیزی باشد، از نوعی به نام “هر” یا “ناشناس” استفاده می کنید. اگر می دانید چیزی همیشه یک رشته است، می توانید از نوع “رشته” استفاده کنید. با این حال، چگونه چیزی را از “هر” به “رشته” تبدیل می کنید؟ اگر _از قبل_ رشته ای باشد چه؟ این همان چیزی است که TypeScript آن را باریک کردن نوع می نامد. حصول اطمینان از انواعی که می گیرید که باریک نیستند و گسترده در نظر گرفته می شوند (الا انواع بسیار متنوعی می تواند باشد)، به یک نوع باریک (فقط 1 یا 2 نوع احتمالاً _است).

بسیاری از این نوع تکنیک‌های باریک‌سازی شبیه اظهارات/بررسی‌های زمان اجرا هستند و برخی نیز چنین هستند. برخی نیستند. هر دو به TypeScript کمک می کنند و شما توسعه دهنده اطمینان حاصل کنید که انواع مطابقت دارند.

با این حال، آنچه واقعاً در مورد آن صحبت نشده است، تبدیل انواع است. بسیاری از این موارد، حداقل در اسناد TypeScript، تحت دامنه محدود کردن نوع در نظر گرفته می شوند، اما نه همه آنها. برخی از آن جاوا اسکریپت اولیه است.

برای مثال، تبدیل یک «هر» به «رشته»، می‌تواند به یکی از 3 روش انجام شود: ادعای نوع، محافظ نوع، یا محمول نوع. ادعا، با استفاده از as کلمه کلیدی، خطرناک ترین است. این شما، توسعه دهنده، هستید که به TypeScript می گویید که چیست، و می تواند 100% به شما اعتماد کند. بررسی نوع دیگری انجام نشده است. به طور موثر بررسی تایپ را نه OFF، بلکه “باور داشتن آنچه شما می گویید” خاموش کنید. بدیهی است، کسی که لذت سیستم‌های تایپ شده و اسمی را تجربه کرده است (برخلاف TypeScript که سخت‌گیرانه است، به تدریج تایپ می‌شود و ساختاری تایپ می‌شود) این را وحشتناک می‌بیند:

const name = json as string
وارد حالت تمام صفحه شوید

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

اکنون، کسانی که از سیستم‌های نوع ساختاری، مانند Java/C#/Go استفاده می‌کنید، ممکن است برخی از این موارد ظریف باشند و آنطور که من ادعا می‌کنم سیاه و سفید نباشند. “اگر راه می رود و مانند اردک صحبت می کند … اردک است”. موارد زیادی وجود دارد که توسعه دهنده، با استفاده از as، صحیح است. گاهی اوقات آنها حتی “به اندازه کافی صحیح” هستند.

مورد دوم، بهتر است، حداقل برای افراد اولیه، یک محافظ نوع است، مانند:

if(typeof json === 'string') {
  console.log("it's a string")
} else {
  console.log("it's not a string")
}
وارد حالت تمام صفحه شوید

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

ترکیب این موارد با هم به شما و TypeScript اطمینان بیشتری می دهد که نوع آن چیزی است که شما واقعاً فکر می کنید. در اینجا میدان های مین زیادی وجود دارد (مثلا typeof [] از این رو “شی” است، نه آرایه Array.isArray موجود)، اما دنیاها بهتر از “من بهتر از کامپایلر می دانم” است.

در نهایت، محمولات نوع وجود دارد. توابعی که true یا false را برمی گرداند. با این حال، این محمولات یک نوع بازگشتی منحصر به فرد دارند. یک ادعای نوع:

function isString(json:any):json is string {
  return typeof json === 'string'
}
وارد حالت تمام صفحه شوید

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

توجه کنید که این رشته “is” است، نه رشته “نمونه”، که همچنین یک میدان مین است. نکته مهم این است که می توانید تعداد زیادی محافظ تایپ را در این توابع قرار دهید تا به TypeScript کمک زیادی کند.

پس چرا وقتی در مورد تبدیل انواع در TypeScript صحبت می کنیم، این 3 تکنیک باریک کردن نوع (بیشتر وجود دارد) اهمیت دارند؟ خوب، احتمالاً می‌توانید ببینید که چگونه می‌توانید با خیال راحت هر یک را به رشته تبدیل کنید… اما در مورد هر یک به یک کاربر بدون چیزی مانند Zod چطور؟

const json:any = ???
// convert the above to this
type User = {
  name: string,
  age: number
}
وارد حالت تمام صفحه شوید

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

شما باید از باریک کردن نوع استفاده کنید تا ابتدا مطمئن شوید که ساختار مورد نیاز شما مطابقت دارد، سپس می‌توانید در صورت امکان با فیلدهای دیگر کد شما را خراب نمی‌کنند (مثلاً Object.keyهایی که فقط 2 فیلد را انتظار دارند، نه بیشتر)، یا آن را با دست جمع کنید، مانند:

if(typeof json === 'object'
  && typeof json.name === 'string'
  && typeof json.age === 'number'
  && isNaN(json.age) === false
  ... // and even more
  return { name: json.name, age: json.age }
وارد حالت تمام صفحه شوید

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

به چند دلیل سخت است:

  1. این مقدار زیادی کد است
  2. این دانش زیادی از جاوا اسکریپت های ابتدایی سطح پایین است
  3. اشکال زدایی این امر مستلزم افزودن آنها به صورت 1 در یک زمان و منتظر ماندن برای سرور زبان TypeScript و بررسی خطاهای کامپایلر برای تنظیم است.
  4. با توجه به میزان کاری که باید انجام دهند، برنامه‌نویس‌ها بلافاصله شروع به مشاهده بازگشت سرمایه (ROI) در تایپ سخت‌گیرانه نمی‌کنند تا به یک سطح اطمینان دقیق و نه صدادار برسند.

… شماره 4 من فکر می کنم قاتل است. من نمی توانم این را تأیید کنم، فقط یک حدس است.

دلایل دیگر ممکن است توسعه دهندگان انواع را به طور ایمن تبدیل نکنند

چیزهای مختلفی وجود دارد که در مبانی کد بزرگتر نیز به این مشکل کمک می کند.

بدون مدل سازی دامنه

اولین مورد عدم مدل‌سازی دامنه از منابع شخص ثالث است، چیزی که ما توسعه دهندگان UI با آن بسیار سروکار دارند و زمانی بدتر می‌شود که خودمان BFF خود را نمی‌نویسیم. وقتی با داده های شخص ثالث مانند JSON از تماس های REST یا Objects در LocalStorage سروکار دارند، برنامه نویس ها به طور پیش فرض به انواع اولیه یا کمتر محدود شده هستند. آنها از رشته یا استفاده خواهند کرد any یا Record. این مسئولیت را بر دوش هر کسی می گذارد که از آن انواع برای محدود کردن بیشتر استفاده می کند.

“ببین مرد، من JSON را تجزیه کردم… شما موفقیت در Observable را دوست ندارید، این مشکل من نیست”.

“چرا از تیم API نپرسید که با چه JSON پاسخ می دهند؟”

“آنها نمی دانند، تیم دارای انتساب است، و من حتی فکر نمی کنم کدی وجود داشته باشد که قابل درک باشد که با آن JSON پاسخ دهد… من فکر می کنم که پیکربندی شده است، بنابراین دیدن سریع آن سخت است.”

“اوه… من…”

توسعه نوع محور چیست؟

دلیل دوم این است که هزاران ادبیات و بحث در مورد توسعه تست محور وجود دارد، اما نه توسعه نوع محور. من سال‌ها در مورد Elixir، Haskell، OCaml، F# و Scala می‌دانستم، و اولین بار زمانی که Elm را یاد گرفتم، نام Type Driven Development را شنیدم. با داشتن این دانش، متوجه شدم که سایر زبان‌های تایپ شده، حتی توسعه‌دهنده‌های Rust، همان کاری را که توسعه‌دهندگان Haskell، OCaml، F# و Scala انجام می‌دهند، انجام می‌دهند: اغلب ابتدا انواع آن‌ها را تعریف می‌کردند، حتی قبل از آزمایش‌ها گاهی اوقات. آنها فقط “آن را انجام دادند” و نامی برای آن نداشتند.

بسیاری از توسعه دهندگان در مورد چیزهایی مانند “غیرممکن کردن موقعیت های غیرممکن از طریق انواع” یا “طراحی مبتنی بر دامنه بدون کلاس” یا “جلوگیری از وسواس اولیه” یا استفاده از انواع برای نفی تعداد واحد تست هایی که باید بنویسید و فعال کردن توانایی نوشتن انواع دیگر تست‌ها مانند تست‌های ویژگی/فازی.

ژنریک ها محبوب تر از صادرات انواع باریک هستند

دلیل سوم این است که بسیاری از چارچوب‌ها و کتابخانه‌ها در چند سال گذشته، علیرغم افزایش عظیم TypeScript، به نظر می‌رسد که انواع محدود را نادیده می‌گیرند. در واقع، بسیاری به دلایل واضح به شدت از ژنریک استفاده کرده‌اند: به توسعه‌دهندگان اجازه می‌دهند که توسط طراحی‌های خود محدود نشوند، بنابراین استفاده از کتابخانه/چارچوب آسان‌تر است. این به فرزندخواندگی کمک می کند، اما به آموزش نوع کمک نمی کند.

برخی حتی در جهت مخالف می روند. در مورد Redux، با وجود تبدیل Redux به Elm به جاوا اسکریپت، و بعداً به TypeScript، هیچ نوع اتحادیه ای یافت نمی شود. تمام هدف اتحادیه ها این است که اطمینان حاصل کنید که Reducers فقط می توانید “1 از 3 کار” را انجام دهید. در عوض، ثابت های رشته یا _شاید_ اگر خوش شانس باشید، یک Enum را خواهید دید. اغلب خطرناک است که از رکوردها برای محدود کردن دامنه استفاده می شود، در حالی که به جای آن باید از اتحادیه استفاده می شد.

نتیجه گیری

من مطمئن نیستم که در مورد عدم نامگذاری انواع غیر از مثال زدن، و قرار دادن انواع در جایی که آنها استفاده می شوند، چه کاری می توان انجام داد.

برای تبدیل نوع، این آسان است: از Zod استفاده کنید.

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

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

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

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