یکی از اهداف مهم استفادهی از TypeScript، یافتن خطاهای متداول کدهای جاواسکریپتی، پیش از اجرای آنها در مرورگر است. برای مثال، قطعه کد زیر:
defaultChecks() {
const author = { firstName: "Vahid", lastName: "N" };
console.log(author.lastname);
author.lastName.trimStart();
author.firstName.charCodeAt("1");
}
دارای سه مشکل است که سریعا توسط TypeScript شناسایی میشود:
- خاصیت lastname در شیء author وجود خارجی ندارد.
- نوع رشتهای، به همراه متد trimStart نیست.
- متد charCodeAt یک عدد را به عنوان پارامتر قبول میکند.
اما باید درنظر داشت که بسیاری از قابلیتهای بررسی کد TypeScript، به صورت پیشفرض فعال نیستند که در ادامه آنها را برای یافتن پیش از موعود بسیاری از مشکلات، فعالسازی خواهیم کرد.
نصب افزونهی TSLint در VSCode
جهت مشاهدهی بهتر خطاهای کامپایلر TypeScript، پیش از کامپایل نهایی کدها، میتوان از افزونهی TSLint استفاده کرد. برای نصب آن، ابتدا باید بستهی ذیل را نصب کرد:
> npm install -g tslint typescript
سپس نیاز است افزونهی آنرا نیز نصب کنید:
https://marketplace.visualstudio.com/items?itemName=eg2.tslint
کار TSLint انجام static code analysis است؛ چیزی شبیه به افزونههایی مانند ریشارپر در ویژوال استودیو که راهنماهایی را در مورد بهتر کردن کیفیت کدهای نوشته شده ارائه میدهد.
فعالسازی بررسی نال و نوعهای نال پذیر
strictNullChecks یکی از مهمترین پرچمهای تنظیمات کامپایلر تایپاسکریپت است. برای افزودن آن، به فایل tsconfig.json مراجعه کرده و پرچم آنرا به true تنظیم کنید:
{
"compilerOptions": {
"strictNullChecks": true
}
}
به این ترتیب کامپایلر تایپاسکریپت، بین null ،undefined و سایر نوعها، تفاوت قائل خواهد شد و اگر نوعی نالپذیر است، باید آنرا به صورت صریح type | null تعریف کنید.
برای مثال، متد ذیل را در نظر بگیرید:
getSessionItem(key: string): any {
const data = window.sessionStorage.getItem(key);
return JSON.parse(data);
}
زمانیکه پرچم strictNullChecks فعال شود، قطعه کد فوق با خطای ذیل کامپایل نخواهد شد:
[ts]
Argument of type 'string | null' is not assignable to parameter of type 'string'.
Type 'null' is not assignable to type 'string'.
const data: string | null
از این جهت که خروجی متد getItem به صورت string | null تعریف شدهاست؛ اما پارامتر متد JSON.parse فقط string را قبول میکند. یعنی ممکن است data دریافتی نال باشد که متد JSON.parse قادر به پردازش آن نیست.
برای رفع این مشکل تنها کافی است بررسی کنیم که آیا data نال است یا خیر؟ و اگر خیر، آنگاه آنرا به متد JSON.parse ارسال کنیم:
getSessionItem(key: string): any {
const data = window.sessionStorage.getItem(key);
if (data) {
return JSON.parse(data);
} else {
return null;
}
}
همانطور که ملاحظه میکنید، فعالسازی strictNullChecks میتواند از بروز بسیاری از خطاهای در زمان اجرای برنامه، جلوگیری کند.
گزارش returnهای فراموش شده
در متد ذیل، یک return فراموش شده وجود دارد و تمام شرطهای برنامه به یک خروجی مشخص، منتهی نمیشوند:
noImplicitReturns(a: number) {
if (a > 10) {
return a;
}
// No return in this branch
}
برای تشخیص زود هنگام یک چنین مشکلاتی میتوان پرچم noImplicitReturns را در فایل tsconfig.json به true تنظیم کرد:
{
"compilerOptions": {
"noImplicitReturns": true
}
}
پس از این تنظیم، کامپایلر در مورد متد noImplicitReturns، خطای ذیل را صادر میکند:
[ts] Not all code paths return a value.
تشخیص کدهای مرده
قطعه کدی که پس از یک return قرار بگیرد، یک کد مرده نامیده میشود. با تنظیم پرچم allowUnreachableCode در فایل tsconfig.json به false، میتوان کامپایلر TypeScript را وادار کرد تا اینگونه موارد را به عنوان خطا گزارش کند:
{
"compilerOptions": {
"allowUnreachableCode": false
}
}
پس از این فعالسازی، کامپایلر TypeScript دو خطای [ts] Unreachable code detected را در مورد قطعه کد ذیل صادر میکند:
allowUnreachableCode() {
if (false) {
console.log("Unreachable code");
}
const a = 1;
if (a > 0) {
return 10; // reachable code
}
return 0;
console.log("Unreachable code");
}
مورد اول مربوط به بدنهی if ایی است که شرط آن false است و این بدنه هیچگاه فراخوانی نمیشود و مورد دوم، به سطر پس از return آخر مرتبط است که آن مورد نیز یک کد مرده محسوب میشود.
تشخیص پارامترها و متغیرهای استفاده نشده
دو متد ذیل را درنظر بگیرید:
unusedLocals() {
const a = "foo"; // Error: 'a' is declared but its value is never read
return "bar";
}
unusedParameters(n: number) {
n = 0; // Never read
}
در اولی متغیر a تعریف شدهاست، اما هیچگاه استفاده نشدهاست. در متد دوم، پارامتر n نیز هیچگاه خوانده نشدهاست و وجود آن بیمصرف است؛ حتی ممکن است نشان فراموش شدن استفادهی از آن و مقدار دهی اشتباه آن باشد.
برای فعالسازی بررسی یک چنین مواردی باید دو پرچم ذیل را در فایل tsconfig.json به true تنظیم کرد:
{
"compilerOptions": {
"noUnusedLocals": true,
"noUnusedParameters": true
}
}
پس از آن، کامپایلر دو خطای ذیل را در مورد متدهای فوق، گزارش میکند:
[ts] 'a' is declared but its value is never read.
[ts] 'n' is declared but its value is never read.
یافتن خواصی که نباید در یک شیء وجود داشته باشند
در مثال ذیل، خاصیت baz در تعاریف اصلی نوعهای x و y وجود ندارد:
excessPropertyForObjectLiterals() {
let x: { foo: number };
x = { foo: 1, baz: 2 }; // Error, excess property 'baz'
let y: { foo: number, bar?: number };
y = { foo: 1, baz: 2 }; // Error, excess property 'baz'
}
برای فعالسازی یافتن یک چنین مواردی که در بسیاری از حالات ممکن است ناشی از اشتباهات تایپی نیز باشند، میتوان پرچم suppressExcessPropertyErrors را در فایل tsconfig.json به false تنظیم کرد:
{
"compilerOptions": {
"suppressExcessPropertyErrors": false
}
}
پس از آن برای نمونه در مورد انتساب اول، یک چنین پیام خطایی از طرف کامپایلر TypeScript صادر خواهد شد:
[ts]
Type '{ foo: number; baz: number; }' is not assignable to type '{ foo: number; }'.
Object literal may only specify known properties, and 'baz' does not exist in type '{ foo: number; }'.
(property) baz: number
یافتن breakهای فراموش شده در عبارات switch
در مثال زیر، یک break فراموش شدهاست:
fallthroughCasesInSwitchStatement(a: number) {
switch (a) {
case 0:
break;
case 1:
a += 1;
case 2:
a += 2;
break;
}
}
برای تشخیص آن توسط کامپایلر TypeScript باید پرچم noFallthroughCasesInSwitch را در فایل tsconfig.json به true تنظیم کرد:
{
"compilerOptions": {
"noFallthroughCasesInSwitch": true
}
}
پس از آن کامپایلر TypeScript خطای «[ts] Fallthrough case in switch» را در مورد متد فوق صادر میکند.
یافتن ایندکسهای تعریف نشدهی در اشیاء
در مثال زیر، شیء x دارای خاصیت b نیست؛ اما دقیقا با این ایندکس مورد استفاده قرار گرفتهاست:
indexingObjectsLackingIndexSignatures() {
const x = { a: 0 };
x["a"] = 1; // ok
x["b"] = 1; // Error, type '{ a: number; }' has no index signature.
}
برای تشخیص یک چنین خطاهایی میتوان پرچم suppressImplicitAnyIndexErrors را در فایل tsconfig.json به false تنظیم کرد:
{
"compilerOptions": {
"suppressImplicitAnyIndexErrors": false
}
}
پس از آن کامپایلر TypeScript خطای ذیل را در مورد دسترسی به ایندکسی که با امضای شیء سازگاری ندارد، صادر میکند:
[ts] Element implicitly has an 'any' type because type '{ a: number; }' has no index signature.
اجبار به تعریف صریح نوعها در TypeScript
عمدهی قابلیت TypeScript در یافتن خطاها به تعاریف نوعها و راهنمایی کامپایلر آن در این زمینه بر میگردد. اما چون این زبان سازگاری کاملی را با JavaScript دارد، تعریف نوعها در آن اجباری نیست و در این حالت اگر نوعی تعریف نشده باشد، به any تفسیر میشود. جهت اجبار به تعریف نوعها در TypeScript میتوان پرچم noImplicitAny را در فایل tsconfig.json به true تنظیم کرد:
{
"compilerOptions": {
"noImplicitAny": true
}
}
در این حالت دیگر قطعه کد ذیل کامپایل نخواهد شد:
noImplicitAny(args) { // Error: Parameter 'args' implicitly has an 'any' type.
console.log(args);
}
برای رفع این مشکل میتوان نوع args را به صورت صریحی مشخص کرد:
noImplicitAnyArgs(args: string[]) { // ok with the type information
console.log(args);
}
یک نکتهی تکمیلی
اگر از دستور ng build --watch برای ساخت برنامههای Angular استفاده میکنید، تغییرات فوق زمانی تاثیر داده خواهند شد که یکبار این برنامه را بسته و مجددا اجرا کنید.