برنامه نویسی

Píldoras TypeScript: تایپ باریک کردن con “as const”

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

برای درک بهتر، اجازه دهید با یک مثال به آن نگاه کنیم.

ما یک نوع داریم Severity به عنوان یک اتحاد رشته ای از مقادیر: “کم”، “متوسط” و “بالا” تعریف می شود.

type Severity = "low" | "medium" | "high"
وارد حالت تمام صفحه شوید

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

می توانستیم بگوییم Severity یک زیرگروه از string که از بین تمام رشته های ممکن، فقط آن سه را پشتیبانی می کند.

از طرف دیگر ما یک شی داریم messages که در آن کلیدها a Severity و مقادیر رشته ها هستند.

const messages: RecordSeverity, string> = {
  low: "😕 not good",
  medium: "😖 ugly",
  high: "😱 AAAAHHH!!!",
}
وارد حالت تمام صفحه شوید

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

ما می خواهیم به مقادیر آن دسترسی داشته باشیم messages و برای این یک متغیر تعریف می کنیم severity با مقدار “کم”.

let severity = "low"

console.log(messages[severity])
//          ^? Error: Element implicitly has an 'any' type because
// expression of type 'string' can't be used to index type 
// 'Record'.
وارد حالت تمام صفحه شوید

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

اگرچه مقداری که به متغیر نسبت داده ایم severity با نوع آن سازگار است Severity، TypeScript خطایی را به ما نشان می دهد که به ما می گوید که کلیدها messages باید از نوع باشد Severity و نه از نوع string.

برای درک اینکه چرا این اتفاق می افتد، ما باید بفهمیم که TypeScript چگونه انواع متغیرهایی را استنباط می‌کند که ما به طور صریح به آنها نوع اختصاص نمی‌دهیم..

اگر در ویرایشگر خود یا در تایپ اسکریپت Playground روی شناور نگه دارید severity خواهیم دید که TypeScript آن را به صورت استنباط می کند string.

این به این دلیل است که ما اعلام کرده ایم severity استفاده كردن let و بنابراین می توانیم مقداری را که با آن سازگار نیست دوباره به آن اختصاص دهیم Severity هر زمان که.

نوع رشته استنباط شده برای متغیر شدت با let اعلام شده است

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

مراقب باشید، من می گویم “در ابتدا” زیرا TypeScript از ما انتظار دارد که مقدار یک متغیر را در زمان اجرا، اما نه نوع آن (از string آ number، و غیره.). اگر قصد ما این است، باید به صراحت آن را مشخص کنیم.

اگر متغیر را با تعریف کنیم، همین اتفاق نمی افتد const.

const severity = "low"

console.log(messages[severity]) // OK
وارد حالت تمام صفحه شوید

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

از آنجایی که استفاده از const اجازه تخصیص مجدد متغیر را نمی دهد، TypeScript نوع تحت اللفظی “low” را استنباط می کند و خطا ناپدید می شود.

نوع تحت اللفظی

بنابراین، به عنوان یک قاعده کلی (به استثنای برخی موارد) TypeScript وسیع ترین نوع را برای متغیرهای نوع اولیه اعلام شده با استنباط می کند let و نوع تحت اللفظی برای متغیرهای اعلام شده با const.

برای متغیرهای نوع ابتدایی بسیار زیاد است، اما چه اتفاقی برای اشیا می افتد؟

بیایید آن را با یک مثال دیگر ببینیم.

ما یک رابط داریم Issue با این خواص:

interface Issue {
  id: string
  severity: Severity
}
وارد حالت تمام صفحه شوید

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

و یک تابع printIssue که یک شی از نوع را دریافت می کند Issue و کاری با آن انجام دهد، که برای ما مهم نیست.

declare function printIssue(issue: Issue): void
وارد حالت تمام صفحه شوید

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

اگر بریم به printIssue یک شی با ساختاری سازگار با Issue، TypeScript خطایی را به ما نشان می دهد که نشان می دهد انواع severity با هم سازگار نیستند.

const issue = {
  id: '123',
  severity: 'low',
}

printIssue(issue)
//         ^? Error: Argument of type '{ id: string; severity:
// string; }' is not assignable to parameter of type 'Issue'. 
// Types of property 'severity' are incompatible.          
وارد حالت تمام صفحه شوید

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

در مورد اشیا صرف نظر از اینکه آنها را با اعلام کنیم let o const، TypeScript نوع ویژگی های خود را به گونه ای استنباط می کند که گویی با آنها اعلام شده اند let.

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

issue.severity = 'other'
وارد حالت تمام صفحه شوید

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

اگر مدتی است که با TypeScript کار می کنیم، می دانیم که می توانیم این مشکل را از چند راه حل کنیم و یکی از آنها استفاده از as const در اموال ناسازگار

const issue = {
  id: '123',
  severity: 'low' as const,
}
وارد حالت تمام صفحه شوید

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

با استفاده از as const ما به TypeScript می گوییم که کوچکترین نوع ممکن را استنتاج کند (باریک شدن) برای ملک severity به جای نوع گسترده تر string. در این صورت نوع تحت اللفظی «کم» استنباط می شود.

استفاده به عنوان const در ملک

یکی دیگر از راه های حل این مشکل، برای اهداف آموزشی، استفاده است Object.freeze در مورد شی

const issue = Object.freeze({
  id: '123',
  severity: 'low',
})
وارد حالت تمام صفحه شوید

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

با توجه به Object.freeze یک شی فقط خواندنی را برمی گرداند، TypeScript نوع تحت اللفظی را برای ویژگی های آن استنباط می کند.

استفاده از Object.freeze در شی اختصاص داده شده به متغیر issue

Object.freeze یک ویژگی دارد که در برخی موارد می تواند یک ایراد باشد و آن این است که تغییر ناپذیری آن این است که “کم عمق“، یعنی فقط روی خصوصیات شی تاثیر می گذارد، اما روی ویژگی های تودرتو آن تاثیر نمی گذارد، که هنوز هم می توان آنها را تغییر داد.

اگر ما علاقه مند به علامت گذاری یک شی به عنوان هستیم readonly در عمق، ما می توانیم اعمال کنیم as const به کل شی

const issue = {
  id: '123',
  severity: 'low',
  another: {
    prop: 'value'
  }
} as const
وارد حالت تمام صفحه شوید

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

و این برای استنتاج نوع و as const به عنوان یک “تکنیک”باریک شدن“، اما ما راه های دیگری برای جلوگیری از استنباط TypeScript انواع گسترده تر از آنچه ما انتظار داریم، داریم، مانند تعیین صریح نوع متغیرها، با استفاده از satisfies یا استفاده از “بررسی اموال مازاد“، اما ما آن را برای یک قرص دیگر رها می کنیم.

اگر تا اینجا پیش رفتید، ممنونم! و اگر شما هم آن را مفید دیدید یا به سادگی آن را دوست داشتید، با اشتراک گذاری آن به من کمک کنید تا به افراد بیشتری دسترسی پیدا کنم 😊

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

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

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

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