let name: string; name = "Vahid"; // OK name = null; // OK name = undefined; // OK
let age: number; age = 24; // OK age = null; // OK age = undefined; // OK
نوع null در TypeScript
همانند JavaScript، نوع null تنها یک مقدار معتبر نال را میتواند داشته باشد و نمیتوان برای مثال یک رشته را به آن انتساب داد. اما انتساب این مقدار به هر نوع متغیر دیگری، سبب پاک شدن مقدار آن خواهد شد. با فعالسازی strictNullChecks، این نوع را تنها به نوعهای نالپذیر میتوان انتساب داد.
نوع undefined در TypeScript
هر متغیری که مقداری به آن انتساب داده نشده باشد، با undefined مقدار دهی میشود. این مورد حتی جهت خروجی متدها نیز صادق است و اگر return ایی در آنها فراموش شود، این خروجی نیز به undefined تفسیر میشود.
در اینجا نیز اگر نوع متغیری به undefined تنظیم شد، این متغیر تنها مقدار undefined را میتواند بپذیرد. تنها با خاموش کردن پرچم strictNullChecks میتوان آنرا به اعداد، رشتهها و غیره نیز انتساب داد.
فعالسازی نوعهای نال نپذیر در TypeScript
برای فعالسازی این قابلیت، نیاز است پرچم strictNullChecks را در فایل تنظیمات کامپایلر به true تنظیم کرد:
{ "compilerOptions": { "strictNullChecks": true } }
برای مثال اگر متدی، رشتهای را به عنوان پارامتر قبول کند، تا پیش از TypeScript 2.0 و فعالسازی strictNullChecks آن، مشخص نبود که رشتهی دریافتی از آن واقعا یک رشتهاست و یا شاید null. اما اکنون یک رشته، فقط یک رشتهاست و دیگر نال پذیر نیست.
let foo: string = null; // Error! Type 'null' is not assignable to type 'string'.
Uncaught ReferenceError: foo is not defined Uncaught TypeError: window.foo is not a function
این مورد برای آرایهها نیز صادق است:
// With strictNullChecks set to false let d: Array<number> = [null, undefined, 10, 15]; //OK let e: Array<string> = ["pie", null, ""]; //OK // With strictNullChecks set to true let d: Array<number> = [null, undefined, 10, 15]; // Error let e: Array<string> = ["pie", null, ""]; // Error
ساده سازی تعریف بررسیهای با پرچم strict، در TypeScript 2.3
تعداد گزینههای قابل تنظیم در فایل tsconfig روز به روز بیشتر میشوند. به همین جهت برای ساده سازی فعالسازی آنها، از TypeScript 2.3 به بعد، پرچم strict نیز به این تنظیمات اضافه شدهاست. کار آن فعالسازی یکجای تمام بررسیهای strict است؛ مانند noImplicitAny، strictNullChecks و غیره.
{ "compilerOptions": { "strict": true /* Enable all strict type-checking options. */ } }
{ "compilerOptions": { "strict": true, "noImplicitThis": false } }
یک نکته: اجرای دستور tsc --init ، سبب تولید یک فایل tsconfig.json از پیش تنظیم شده، بر اساس آخرین قابلیتهای کامپایلر TypeScript میشود.
اما ... اکنون چگونه یک نوع را نالپذیر کنیم؟
TypeScript به همراه دو نوع ویژهی null و undefined نیز شدهاست که تنها دارای مقادیر null و undefined میتوانند باشند. به این معنا که در حین تعریف نوع یک متغیر، میتوان این دو را نیز ذکر کرد و دیگر تنها به عنوان دو مقدار مطرح نیستند. به این ترتیب میتوان از آنها یک union type را ایجاد کرد:
let foo: string | null = null; // Okay!
let name: string | null; name = "Vahid"; // OK name = null; // OK name = undefined; // Error
یک نکته:
تا پیش از این اگر متغیری را به این صورت تعریف میکردیم:
let z = null;
بررسی انتساب، پیش از استفاده
با فعالسازی strictNullChecks، اکنون کامپایلر برای تمام نوعهایی که undefined نیستند، یک مقدار اولیه را پیش از استفادهی از آنها درخواست میکند:
testAssignedBeforeUseChecking() { let x: number; console.log(x); }
[ts] Variable 'x' is used before being assigned.
اما در حالت ذیل، عدد z میتواند عدد و یا undefined باشد؛ به همین جهت کامپایلر با استفادهی از آن مشکلی نخواهد داشت:
let z: number | undefined; console.log(z);
یک نکته: خواص و پارامترهای اختیاری، به صورت خودکار دارای نوع undefined نیز هستند. برای مثال امضای متد ذیل:
method1(x?: number) { }
method1(x?: number | undefined) { }
اجبار به بررسی نال نبودن مقادیر، پیش از استفادهی از آنها در متدهای نال نپذیر
اگر پارامتر متدی یا خاصیت شیءایی نال پذیر نباشند، با ارسال مقدار نوعی به آنها که میتواند null و یا undefined را بپذیرد، یک خطای زمان کامپایل صادر خواهد شد. در اینجا محافظهای نوعها توسعه یافتهاند تا اگر بررسی نال یا undefined بودن مقداری انجام شد، مشکلی در جهت استفادهی از آنها نباشد:
f(x: number): string { return x.toString(); } testTypeGuards() { let x: number | null | undefined; if (x) { this.f(x); // Ok, type of x is number here } else { this.f(x); // Error, type of x is number? here } }
Argument of type 'number | null | undefined' is not assignable to parameter of type 'number'. Type 'undefined' is not assignable to type 'number'.
امکان این بررسی در مورد عبارات شرطی نیز صادق است:
getLength(s: string | null) { return s ? s.length : 0; }
توسعهی محافظهای نوعها جهت کار با نوعهای نال نپذیر
در مثال ذیل، خروجی متد isNumber دارای امضایی به همراه is است:
isNumber(n: any): n is number { // type guard return typeof n === "number"; }
usedMb(usedBytes?: number): number | undefined { return this.isNumber(usedBytes) ? (usedBytes / (1024 * 1024)) : undefined; }
usedMb2(usedBytes?: number): number | undefined { return usedBytes ? (usedBytes / (1024 * 1024)) : undefined; }
همچنین امضای متد نیز به number | undefined تغییر یافتهاست. در غیر اینصورت، خطای زمان کامپایل Type undefined is not assignable to type number صادر خواهد شد.
در حین استفادهی از یک چنین متدی، دیگر نمیتوان به خروجی آن به صورت ذیل دسترسی یافت:
formatUsedMb(): string { //ERROR: TS2531: Object is possibly undefined return this.usedMb(123).toFixed(0).toString(); }
formatUsed(): string { const usedMb = this.usedMb(123); return usedMb ? usedMb.toFixed(0).toString() : ""; }
لغو بررسی strictNullChecks به صورت موقت
با استفاده از اپراتور ! میتوان به کامپایلر اطمینان داد که این متغیر یا خاصیت، دارای مقدار نال نیست و نخواهد بود:
export interface User { name: string; age?: number; }
printUserInfo(user: User) { if (user.age != null) { console.log(`${user.name}, ${user.age.toString()}`); } }
printUserInfo(user: User) { console.log(`${user.name}, ${user.age.toString()}`); }
و یا میتوان توسط اپراتور ! این بررسی را به صورت موقت خاموش کرد:
printUserInfo(user: User) { console.log(`${user.name}, ${user.age!.toString()}`); }
[tslint] Forbidden non null assertion (no-non-null-assertion)
یک نکتهی تکمیلی
پس از آزمایش موفقیت آمیز نوعهای نال نپذیر در TypeScript، مایکروسافت قصد دارد این ویژگی را به C# 8.0 نیز در مورد نوعهای ارجاعی که میتوانند نال پذیر باشند، اضافه کند (امکان داشتن نوعهای ارجاعی نالنپذیر).