برنامه نویسی

انواع TypeScript دروغ می گویند و چگونه آنها را بهبود می بخشد

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

چرا مراقبت

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

بنابراین معاملات تجاری به شرح زیر است.

جوانب:

  • کد کمتری برای حفظ
  • تست های واحد کمتر برای نوشتن
  • اعتماد به نفس بیشتر کد دارای استثنائات زمان اجرا و نشانگرهای تهی نخواهد بود
  • می تواند اطمینان حاصل کند که شرایط غیرممکن با استفاده از انواع نمی تواند رخ دهد تا وضعیتی را که توسعه دهنده به آن اهمیت می دهد ، محدود کند

منفی:

  • برای خواندن بیشتر در کد شما باید انواع را بخوانید
  • خطاهای کامپایلر می تواند به همان اندازه چرمی های چاق یا قرمز مانند آثار پشته باشد
  • انواع ، مانند کد ، قابل درک است
  • انواع همیشه دقیق نیستند

آخرین نتیجه مهم است و آنچه در این مقاله پوشش خواهیم داد. نکته اصلی سرمایه گذاری در نوشتن انواع این است که وقتی برنامه کامپایل می شود ، کار می کند و جای تعجب ندارد.

… اما TypeScript به تدریج تایپ می شود ، بنابراین گالن های ظریف در اینجا وجود دارد.

اگر شما/تیم خود را با استفاده از انواع ، با استفاده از سرعت کامپایلر خریداری کنید و از انواع خود برای محدود کردن دامنه مشکل خود استفاده کنید ، می تواند یک خالص باشد. من متوجه شده ام که بسیاری از تیم ها در چقدر سختگیرانه می خواهند متفاوت باشند. من به شما پیشنهاد می کنم که موارد زیر را به عنوان حداقل لخت برای پایه های کد Greenfield / Brownfield دنبال کنید ، در غیر این صورت من این سؤال را می کنم که چرا از TypeScript استفاده می کنید و در عوض فقط از JavaScript استفاده می کنید. برای پروژه هایی که از JavaScript به TypeScript مهاجرت می کنید ، تفاوت های ظریف مختلفی وجود دارد.

“یک نوع دروغ” چیست؟

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

بیایید ابتدا به یک نوع حقیقت نگاه کنیم.

const greet = (name:string):string =>
  `Hello ${name}!`
حالت تمام صفحه را وارد کنید

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

اگر در یک رشته عبور کنیم ، می خواهیم یک رشته را برگردانیم. بنابراین این مجموعه ای از انواع کاملاً راستگو است. حال بیایید ببینیم که یک نوع دروغ چیست.

کد زیر را در نظر بگیرید:

type Person = { name: string }
const parsePeople = (jsonString:string):Array<Person> => {
  return JSON.parse(jsonString) as Array<Person>
}
حالت تمام صفحه را وارد کنید

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

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

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

مسیر شاد به این شکل به نظر می رسد:

const people = parsePeople('[ { "name": "Jesse" } ]')
حالت تمام صفحه را وارد کنید

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

با این حال ، حداقل 2 مسیر ناراضی وجود دارد که انواع آنها را پوشش نمی دهند. در مورد تجزیه یک گاو چطور؟

const people = parsePeople('🐄')
حالت تمام صفحه را وارد کنید

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

این یک استثناء زمان اجرا را پرتاب می کند. چند مشکل در این مورد

بدون نوع استثنا

انواع نشان نمی دهد که عملکرد می تواند خطایی ایجاد کند. این می گوید که Array، اما باید در عوض بود Array | never یا Array | undefined برای نشان دادن آن می تواند هر یک مجموعه ای از افراد (ها) را برگردانید ، یا هرگز برنگردید زیرا خطایی به وجود آورد.

احتیاط: شما نمی توانید قرار دهید never در انواع اتحادیه مانند این ، هرگز مانند یک نوع 0 عمل نمی کند. این مانند اضافه کردن 1 به 0 است. شما در نهایت با 1. بنابراین Array | never در پایان بودن Array تا آنجا که به TypeScript مربوط می شود. برخی از توسعه دهندگان هرگز از این امر برای برقراری ارتباط قصد استفاده نخواهند کرد ، اما این اشتباه است زیرا کامپایلر به شما امکان می دهد سناریوی خطا را نادیده بگیرید.

بدون نوع باریک

این کد را در نظر بگیرید:

const people = parsePeople('["🐄", "🐄", "🐄"]')
console.log(people[0].name)
حالت تمام صفحه را وارد کنید

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

به جای یک نام رشته ای نامشخص را چاپ می کند. این امر به این دلیل است که آنها به جای انجام باریک شدن نوع لازم ، از ادعاهای نوع TypeScript استفاده نمی کنند. انواع می گویند “ما مجموعه ای از اشیاء شخص را می گیریم” ، اما آنچه در واقع اتفاق افتاد این بود که ما یک آرایه پر از گاوهای رشته ای گرفتیم. انواع دوباره دروغ می گویند یا اگر ما آنها را شخصیت نمی کنیم ، آنها فقط مسیر شاد را پوشش می دهند.

می توانید بررسی کنید که آیا این اشیاء در زمان اجرا در واقع هستند Person انواع به جای چیز دیگری با استفاده از باریک بودن نوع ، یا بهتر از آن چیزی مانند Zod یا Arktype.

انواع دقیق تر

بنابراین چگونه می توانیم این نوع را بهبود بخشیم؟ هیچ تایپ خوبی وجود ندارد که من در TypeScript دیده ام که به شما امکان می دهد یک عملکرد را نشان دهد که می تواند خطایی را به استثناء بررسی شده جاوا برساند. بنابراین ما باید خطاها را به عنوان مقادیر رفتار کنیم. مقادیر را می توان تایپ کرد ، و می توان بازگردانده شد در حالی که استثنائات عملکرد را از اجرای و در نتیجه بازگشت متوقف می کنند. این بدان معناست که پرتاب استثنائات به طور عمدی خطاهایی را ایجاد می کند که نمی توانیم با کامپایلر Typecript بدست آوریم ، و بنابراین نوع کمتری را ایمن و در نتیجه بدتر می کند. در عوض ، ما باید خطاها را برگردانیم.

داده ها یا هیچ چیز زیرا همه چیز خراب است

ما می توانیم به Array | undefined که در آن undefined نشان می دهد که چیزی اشتباه پیش آمده است. این ممکن است خوب باشد بسیاری از خطاها که رخ می دهد قابل بازیابی نیست ، و جزئیات ظریف همیشه در UI مفید نیست زیرا کاربر واقعاً نمی تواند برای رفع مشکل خود کاری انجام دهد.

بنابراین قسمت اول رفع JSON.parseبشر اگر به Typescript نگاه کنید ، JSON.parse به عنوان تایپ می شود (value:string) => any اما ارزش بازگشت آن در واقع چیزی شبیه است unknown | throw Exceptionبشر بنابراین ما برای رسیدگی به استثناء احتمالی JSON.Parse که در آن شکست می خوریم ، مقداری کد می نویسیم:

try {
  const result = JSON.parse(jsonString)
  ...
  return result as Array<Person>
} catch(error) {
  // log error
  return undefined
}
حالت تمام صفحه را وارد کنید

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

در مرحله بعد ، ما باید اطمینان حاصل کنیم که پوشش های نامشخص موردی که Json.Parse موفقیت آمیز بود ، اما JSON ما به شکلی نبود که انتظار داشتیم از مجموعه ای از سوابق مردم انتظار داشته باشیم. در حال حاضر ، ما از چیزی مانند ZOD در حالت امن استفاده می کنیم که اگر داده های ما با نوع ما مطابقت نداشته باشد ، استثنائی را به وجود می آورد. از آنجا که ما در حال حاضر در یک بلوک امتحان/گرفتن هستیم ، این انفجار را نیز به همراه خواهد داشت.

const result = JSON.parse(jsonString)
return ZodArrayOfPeople.parse(result)
حالت تمام صفحه را وارد کنید

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

چه اشتباهی رخ داد؟

مسئله این است که انواع به ما نمی گویند چه چیزی در واقع وقتی عملکرد شکست خورد اشتباه شد. اگر می خواهیم این سیستم را به کنسول وارد کنیم ، یا به یک سیستم ورود به سیستم شخص ثالث / هشدار / رعایت ، نیاز داریم تا انواع آن اطلاعات را داشته باشیم.

برای حل این مسئله ، می توانیم نوع اصلی/نتیجه ای ایجاد کنیم. مثل این است Promise، اما همزمان وعده نشان می دهد که یک تابع می تواند یا (کلمه “یا” را در آنجا یادداشت کنید) با یک مقدار با موفقیت باز می گردد ، یا شکست می خورد. ما Result به همان روش استفاده شود.

type Result
  = Ok
  | Err
حالت تمام صفحه را وارد کنید

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

در Ok به نظر می رسد { type: 'Ok' } وت Err به نظر می رسد { type: 'Err' }بشر در type یک تبعیض است و به TypeScript اجازه می دهد تا تفاوت بین 2 نوع را بیان کند.

اگر امضای عملکرد خود را به:

const parsePeople = (jsonString:string):Result =>
حالت تمام صفحه را وارد کنید

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

خوب است ، اما یک Ok مقدار بازگشت در واقع داده های ما را به ما نمی دهد ، و Err نشان می دهد خطایی رخ داده است ، اما نه چه خطا رخ داد و چرابشر تثبیت Err بسیار ساده است فقط یک رشته در داخل آن داشته باشید زیرا بیشتر خطاها را می توان به رشته ای که می توانیم بخوانیم و به هر سیستم ورود به سیستم ارسال کنیم ، کاهش داد.

type Err = { type: 'Err', error: string }

برای Ok، اگرچه … باید هر چیزی باشد که توسعه دهنده بخواهد. برای اجازه دادن به آن ، ما نیاز خواهیم داشت Ok داشتن یک پارامتر نوع ؛ دقیقاً مانند توابع دارای پارامترهایی هستند ، انواع آنها نیز می توانند آنها را داشته باشند.

type Ok<T> = { type: 'Ok', data: T }
حالت تمام صفحه را وارد کنید

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

اگر تا به حال دیده اید Promise یا Promise>، پس شما Ok می تواند شبیه به هم باشد ، مانند Ok به این معنی است که شما با یک رشته در داخل آن خوب هستید.

اکنون می توانیم امضای عملکرد خود را به روز کنیم:

const parsePeople = (jsonString:string):Result<Array<Person>> =>
حالت تمام صفحه را وارد کنید

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

بدنه عملکرد چیزی شبیه به این است:

try {
  const result = JSON.parse(jsonString)
  const data = ZodArrayPerson.parse(result)
  return { type: 'Ok', data }
} catch(error) {
  // log error
  return { type: 'Err', error: error.message }
}
حالت تمام صفحه را وارد کنید

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

این به روزرسانی عالی است که کد ما دیگر به طور غیر منتظره ای منفجر نمی شود ، و اگر JSON را تجزیه کنیم ، و این کار می کند ، انواع مختلفی را که انتظار داریم دریافت می کنیم ، اما اگر JSON نتواند تجزیه شود ، یا انواع اشتباهی را دریافت می کنیم ، عملکرد خطایی را با چرا در داخل باز می گرداند. این به نوبه خود تضمین می کند کسانی که از این عملکرد استفاده می کنند مجبور به رسیدگی به مسیر ناراضی هستند ، در غیر این صورت کد آنها کامپایل نمی شود. از آنجا که ما از یک اتحادیه تبعیض آمیز استفاده کردیم ، کد آنها برای دسترسی با ایمن فقط به data در مسیرهای شاد ، و فقط error در مسیرهای ناراضی

const result = parsePeople(...)
if(result.type === 'Ok') {
  console.log("people:", result.data)
} else {
  console.log("error:", result.error)
}
حالت تمام صفحه را وارد کنید

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

آزمایش ، پوشش کد و بینش بهتر ورود به سیستم

با این حال ، 3 مشکل باقی مانده است که ما می توانیم برای بهتر شدن انواع حل کنیم.

اول ، آزمایش کمی خسته کننده است. شما باید در هر دو JSON BAD یا JSON معتبر عبور کنید اما نوع اشتباهی برای به دست آوردن 2 مسیر ناراضی است. مسیرهای ناراضی هر دو اداره می شوند ، اما از نوع بازگشت مشخص نیست که چه اشتباهی می تواند انجام شود. شما باید کد را بخوانید تا بدانید که ورودی های رشته ای برای تطبیق انواع. این می تواند به عادت های بد ادعا در رشته ها در تست های واحد برای اعتبارسنجی مسیرهای شاد منجر شود. اگر از انواع استفاده می کنید ، لازم نیست آن تست ها را بنویسید ، حداقل در مورد انواع ادعا کنید که کامپایلر می تواند به رشته های جادویی کمک کند. ما Err یک رشته در داخل است ، اما یک رشته می تواند هر چیزی باشد ، بعداً تغییر کرده و کامپایل به شما نمی گوید ، فقط آزمایشات واحد خواهد بود. در حالی که خوب است ، این باعث می شود تست ها برای تغییرات شکننده شوند.

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

سرانجام ، در حالی که من این بینش ورود به سیستم را می نامم ، در این نوع فرضیه وجود دارد که با 2 موقعیت مختلف مشکل ساز بد JSON یا شکل نادرست JSON هیچ کاری انجام نمی شود. برخی از کاربران این عملکرد ممکن است باید این را بدانند ، اما در حال حاضر راهی برای انجام این کار ندارند. چه جلو و چه در عقب ، خطاها به سیستم عامل های مشاهده مانند New Relic یا Splunk وارد می شوند ، هشدارها برای آن خطاها تنظیم می شوند و تلفن شما ساعت 3 صبح حلقه می کند. شما از خودتان تشکر خواهید کرد که خطاها به وضوح مشخص شده اند در مقابل “برو وقتی که نیمه بیدار هستید و همه در حال وحشت هستند ، به ردیابی پشته در JSON نگاه کنید.”

ما می توانیم با تغییر نوع بازگشت ، همه 3 را اصلاح کنیم undefined به “اینجا اتفاقی افتاده است”. انواع ، تا حدودی ساخته شده است تا به باریک شدن چیزها کمک کند. بنابراین بیایید نوعی را تعریف کنیم که نشان می دهد 1 از این 3 مورد می تواند اتفاق بیفتد:

type ParsePeopleResult
  = Success<Array<Person>>
  | FailedToParse<string>
  | DecodingError<string>
حالت تمام صفحه را وارد کنید

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

این یک نوع واحد است که عملکرد باز می گردد. گزینه دیگر شامل 2 خطا در نتیجه است Err، اما به طور معمول Result.Err فقط به عنوان یک رشته رفتار می شود ، و اگر می خواهید با خطای ریز و درشت ریزتر استفاده کنید ، نوع خود را درست می کنید.

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

it('should work with good JSON', () => {
  const { type } = parsePeople(JSON.stringify({ name: 'Jesse' })
  expect(type).toBe('Success')
})
حالت تمام صفحه را وارد کنید

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

مسیر ناخوشایند Json:

it('should handle bad JSON', () => {
  const { type } = parsePeople('🐄')
  expect(type).toBe('FailedToParse')
})
حالت تمام صفحه را وارد کنید

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

و در آخر مسیر بد نوع ناراضی:

it('should handle bad data', () => {
  const { type } = parsePeople(JSON.stringify([1, 2, 3]))
  expect(type).toBe('DecodingError')
})
حالت تمام صفحه را وارد کنید

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

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

const result = parsePeople(...)
switch(result.type) {
  case 'Success':
    ...
  case 'FailedToParse':
    logger(`Failed to Parse JSON, reason: ${result.error}`)
  case 'DecodingError
    logger(`Failed to decode the successfully parsed JSON to an Array of Person's, reason: ${ersult.error}`) 
حالت تمام صفحه را وارد کنید

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

اکنون ، ما parsePerson عملکرد را می توان با امضای زیر به روز کرد:

const parsePerson = (jsonString:string):ParsePeopleResult => 
حالت تمام صفحه را وارد کنید

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

… و دروغ نمی گوید.

نتیجه گیری

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

در آغوش خطاها به عنوان مقادیر بازگشت به جای پرتاب استثنائات می تواند پیش بینی کد شما را به شدت بهبود بخشد ، و به TypeScript کمک کند تا یک کدهای بزرگ را پوشش دهد تا اطمینان حاصل شود که پس از کامپایل و اجرای آن ، ناآگاهانه خراب نمی شود.

استفاده از کتابخانه های باریک یاور نوع اهرم مانند ZOD یا Arctype می تواند نوع باریک شدن نوع مورد نیاز شما را به طور قابل توجهی کاهش دهد ، تجزیه و تحلیل داده های خارج از کشور ، کاهش مقدار زیادی کد باریک کننده نوع دیگ بخار و افزایش ایمنی نوع را کاهش می دهد. بسیاری از Type Lies TypeScript از json.parse است ، خطاهای تبدیل شده به عنوان ناشناخته یا هنگام خواندن پرونده های پیکربندی محلی. اینجاست که Zod/Arctype واقعاً می تواند تأثیر مثبت بزرگی بگذارد.

هر کجا می بینید anyبا unknown، و به ویژه as کلمه کلیدی که در آن برنامه نویس ادعا می کند که بهتر از کامپایلر می داند ، از این 3 به عنوان میوه کم آویز در ایمن تر کردن کد شما احتیاط کنید.

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

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

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

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