از تشخیص کلیک خارج از کامپوننت تا Type Assertions در تایپ‌اسکریپت

گاهی اوقات پیش میاد که باید بیشتر به Typescript بفهمونید که تایپ متغییرتون چیه و همین‌جوری نمیفهمه و با ارور کامپایلر مواجه می‌شید. اینجاست که Type Assertions به کمک شما میاد و مشکلتون را حل میکنه. Type Assertions از ورژن 1.6 به بعد تایپ‌اسکریپت در دسترسه و می‌‌تونید ازش استفاده کنید.

قرار بود با react.js یه کامپوننت طراحی کنم که یک حالت مهم در اون کامپوننت زمانی اتفاق می‌افتاد که خارج از کامپوننت کلیک می‌شد. کلا تشخیص اینکه خارج از کامپوننت کلیک شده یه موضوع پر بحث در react.js هست. برای حل این چالش روش های مختلفی وجود داره حتی یه پکیج پرطرفدار برای آن ساخته شده است. من با یکمی تحقیق فهمیدم که بهترین و سریع‌ترین روش ممکن استفاده از تکنیک فوکوس/بلور است. چون هیچگونه EventListenerای به کامپومننت اضافه نمی‌کنه بالاترین پرفورمنس را به ما می‌ده.

مشکل اینجا به وجود اومد که من داخل کامپوننتم از یه تگی استفاده کرده بودم که position آن absolute و اندازه‌ی آن بزرگتر اندازه‌ی تگ پدرش (parent) بود و بخاطر ساز و کار مرورگر (یا باگ) زمانی که روی آن تگ absolute کلیک می‌شد بجای اینکه تگ پدرش focus شود تگ پدرش blur می‌شد. با یکمی سرچ فهمیدم که باید یه فیلتر سر راه تگ پدر (parent) بزارم تا مشکلش حل شود.

اینجا دیگه مشکلم حل شده بود و به راحتی می‌تونستم کلیک کردن خارج از کامپوننت را تشخیص بدم. ولی تایپ‌اسکریپت یه ارور می‌داد که خیلی رو مخ بود:

مشکل اینه که تابع event.currentTarget.contains() یه آرگمان می‌پذیره که باید از نوع Node باشه ولی event.relatedTarget که به عنوان ورودی به تابع داده‌ایم تایپش EventTarget است. چون EventTarget یک تایپ عمومی است و Node یکی از زیر مجموعه های آن است.

Basically EventTarget is the most general type, of which Element is a subtype, and HTMLElement is a subtype of that. If you get back a thing from the DOM, we generally have no idea which it is, and you should add a type assertion to "add in" the specific external knowledge that you have about the structure of your particular DOM layout.It's possible, for example, that the .target of an event is not an Element (you can add event listeners to XMLHttpRequest, for example, and XMLHttpRequest does not have a getBoundingClientRect method).

ولی اینجا مطمئن هستیم که EventTarget از نوع Element یا همون Node هست. بنابراین باید به صورت صریح به typescript بفهمونیم اینجا EventTarget از نوع Node است.

Type assertions

با استفاده از Type assertions می‌تونیم به تایپ‌اسکریپت به صورت صریح اعلام کنیم که من مطئمنم تایپ متغییر اینه و بهمون ارور نده لطفا. مثل Type Casting در بقیه‌ی زبانهای برنامه‌نویسی با این تفاوت که در run-time هیچ اتفاقی نمی‌افته. سینتکس Type assertions به صورت زیر است:

<Type> UnaryExpression

چون سینتکس بالا با JSX (و TSX) تداخل داره می‌تونیم از سینتکس جایگزین زیر استفاده کنیم:

UnaryExpression as Type

اگه ما از Type assertions استفاده کنیم و به صورت صریح اعلام کنیم که تایپ event.relatedTarget از نوع Node است کامپوننت نهایی ما به صورت زیر می‌شود و هیچگونه اروری دریافت نمی‌کنیم ?