Type Coercion / Conversion

মঙ্গলবার, ২৭ এপ্রিল ২০২১, দুপুর ১০:১৮ সময়

আমরা অনেক সময় জাভাস্ক্রিপ্টে বিভিন্ন ধরনের আজব ছিন্টাক্স দেখতে পাই আর সব কিছুই যেন আমাদের মাথার উপর দিয়ে যায় 🙄। এর কারন কি? আমরা জানি যে, জাভাস্ক্রিপ্ট অনেক স্মার্ট তবে এটা জানি না যে, সে কিভাবে স্মার্টলি কাজ করে। জাভাস্ক্রিপ্ট যেহেতু একটি হাই লেভেল ল্যাংগুয়েজ তাই এ নিজ থেকেই অনেক ক্ষেত্রে টাইপ কোরসান বা কনভার্সন করে থাকে, যা আমাদের না জানা থাকলে অনেক ছিন্টাক্স আমাদের মাথার উপর দিয়েই যাবে, এটা অস্বাভাবিক কিছু না।

তাই কোন ফাংশান কল না করেই জাভাস্ক্রিপ্ট কিভাবে টাইপ কানভার্সন ঘটায়, এটা যদি আমরা জানি, তাহলে আমরা ঐসব আজব সিন্টাক্স খুব সহজেই বুঝতে পারব যে কিভাবে এইসব ছিন্টাক্স টাইপ কনভার্সন ঘটাচ্ছে।

আমি আমার আর্টিকেলে জাভাস্ক্রিপ্টের টাইপ কোরসান বা কনভার্সন নিয়ে যথেষ্ট উপস্থাপন করার চেষ্টা করেছি। যা পড়লে আপনি জাভাস্ক্রিপ্টের টাইপ কানভার্সন যথেষ্ট জানতে পারবেন বলে আমি আশা করি।

- fahim faisaal

Type Coercion কি?

জাভাস্ক্রিপ্টে Type Coercion আর অন্যান্য লাঙ্গুয়েজে একে বলা হয় Type Conversion যার কাজ হল কোন ডাটাকে একটি ডাটা টাইপ থেকে অন্য একটি ডাটা টাইপে রুপান্তর করা। যেমন - String to Number, Number to String, Number to boolean ইত্যাদি।

আমরা জানি জাভাস্ক্রিপ্ট এ মোট প্রিমিটিভ ডাটা টাইপ ৭ টি -

  1. String
  2. Number
  3. Boolean
  4. null
  5. undefined
  6. Symbol ( added in EcmaScript6 )
  7. BigInt ( added in EcmaScript 2020 11th Edition )

তবে null প্রিমিটিভ ডাটা টাইপ হলেও যদি আমরা typeof অপারেটর দিয়ে দেখি তাহলে জাভাস্ক্রিপ্ট console আমাদের বলে যে এটি একটি object.

console.log(typeof null); // 'object'

তবে null প্রিমিটিভ ডাটা টাইপেই ব্যবহার হয়।

জাভাস্ক্রিপ্টে যেকোন ডাটা টাইপকে কনভার্ট করলে তা মূলত তিনটি টাইপে কনভার্ট হয় -

  1. to String
  2. to Number
  3. to Boolean

তবে EcmaScript 11th Edition প্রকাশিত পাওয়ার পর জাভাস্ক্রিপ্ট আমাদের জন্য আর একটি ডাটা টাইপ আনে যার নাম → BigInt কোন নাম্বারের শেষে n যুক্ত করলে সেই নাম্বার BigInt হয়ে যায়। তবে BigInt কখনও floating number বা দশমিক সংখ্যা হয় না →

let bigInteger = 2n;
let floatBigInt = 2.1n;
console.log(typeof bigInteger, bigInteger); // bigint 2n
console.log(floatBigInt); // Uncaught SyntaxError: Invalid or unexpected token

আমরা যখন কোন Built in Constructor দিয়ে Type Coercion করি তখন আমরা সহজে বুঝতে পারি যে আমরা কোন্‌ ডাটা টাইপ থেকে কোন্‌ ডাটা টাইপে রুপান্তর করছি। যেমন →

const numberToString = String(1234);
console.log(typeof numberToString, numberToString); // String '1234'

const stringToNumber = Number('1234');
console.log(typeof stringToNumber, stringToNumber); // Number 1234

const numberToBoolean = Boolean(1);
console.log(typeof numberToBoolean, numberToBoolean); // Boolean true

let numberToBigInt = BigInt(1234);
console.log(typeof numberToBigInt, numberToBigInt); // bigint 1234n

উপরের কোড দেখে আমরা সহজেই বুঝতে পারতেছি যে, কোন ডাটা টাইপ কিসে কনভার্ট হচ্ছে। তবে যেহেতু জাভাস্ক্রিপ্ট একটি weakly typed language সেহেতু সে নিজ থেকেও বিভিন্ন কন্টেক্সটে type conversion করে থাকে।

Kind of Type Coercion -

জাভাস্ক্রিপ্টে coercion / conversion মূলত দুই প্রকার - 

  1. Explicit Conversion
  2. Implicit Conversion

Explicit Conversion যখন আমরা কোন Built in Construtor বা function এর দ্বারা Declare করে দেই যে ডাটার টাইপটি String হবে, না Number হবে না Boolean হবে, না BigInt হবে তখন যেই Conversion টা ঘটে তাকে Explicit Coercion বলে। যেমন →

console.log(String(1234)); // '1234'
console.log(Boolean(0)); // false
console.log(Number('1234') // 1234
console.log(String(true)) // 'true'

Implicit Conversion  কোন Built in Construtor অথবা function Declaration ছাড়া জাভাস্ক্রিপ্ট যখন automatically কিছু অপারেটর যেমন →
 +, -, *, /,  %,  <,  ≤,  ≥,  >,  ==,  ≠,  !,  ।,  &,  ~,  ^  এর মাধ্যমে কনভার্সন ঘটায় তখন তাকে Implicit Coercion বলে। যেমন →

console.log('I am ' + 22 ) // 'I am 22'
console.log(+'1234') // 1234
console.log(0 < '10') // true
console.log('10' + ['x']) // '10x'

উপরের দুইটি উদাহরণ থেকে আমরা সহজে বুঝতে পারলাম যে Explicit Coercion & Implicit Coercion এর মধ্যে পার্থক্যটা কি।

তবে একটা কথা না বললেই নয় যে, আমরা যদিও Implicit Coercion করি তবে আমাদের জাভাস্ক্রিপ্ট ইঞ্জিন পর্দার অন্তরালে Explicit Coercion ই ঘটায়। তাহলে প্রশ্ন আসে যে, আমরা Implicit Coercion কেন্‌ ব্যবহার করব? করব, কারন এইটাতে অল্প কোড লিখতে হয় আর হাজারও ডেভ্লপার এই Implicit Coercion ব্যবহার করে। তাই আমাদেরও জানতে হবে।

Explicit Coercion এর বাপারে সম্পূর্ণ ধারনা থাকলে Implicit Coercion জানতে বা আয়ত্ত করতে বেশি সময় লাগবে না। তাই আমাদের জাভাস্ক্রিপ্ট ইঞ্জিন কিভাবে Coercion টা ঘটায় তা জানার জন্য Explicit Coercion টা ভালভাবে জানতে হবে।

Explicit conversion to String -

//* Convert to String
let numberPositive = String(12345);
console.log(typeof numberPositive, numberPositive); // string '1234'

let numberNegative = String(-12345);
console.log(typeof numberNegative, numberNegative); // string '-1234'

let boolean = String(true);
console.log(typeof boolean, boolean); // string 'true'

let fn = String(function () {});
console.log(typeof fn, fn); // string 'function () {}'

let object = String({});
console.log(typeof object, object); // string '[object object]'

let array = String([]);
console.log(typeof array, array); // string ''

let Null = String(null);
console.log(typeof Null, Null); // string 'null'

let Undefined = String(undefined);
console.log(typeof Undefined, Undefined); // string 'undefined'

let bigIntToString = String(1n);
console.log(typeof bigIntToString, bigIntToString); // string '1'

উপরের কোড থেকে জানা জাচ্ছে যে, যেকোন ডাটা টাইপকেই আমরা String এ কনভার্ট করতে পারব এই String() function এর মাধ্যমে, এখন দেখা যাক জাভাস্ক্রিপ্ট Number এ কিভাবে কনভার্ট করে।

Explicit conversion to Number -

let validNumberString = Number("1234"); // input can also -1234
console.log(typeof validNumberString, validNumberString); // number 1234

let validFloatingNumber = Number("1234.567"); // input can also -1234.567
console.log(typeof validFloatingNumber, validFloatingNumber); // number 1234.567

let backslash = Number("\n"); // input can aslo \t \r ""
console.log(typeof backslash, backslash); //number 0

let spaceNumbers = Number("   1234   ");
console.log(typeof spaceNumbers, spaceNumbers); // number 1234

let booleanNumTrue = Number(true);
console.log(typeof booleanNumTrue, booleanNumTrue); // number 1

let booleanNumFalse = Number(false);
console.log(typeof booleanNumFalse, booleanNumFalse); // number 0

let NumberNull = Number(null);
console.log(typeof NumberNull, NumberNull); // number 0

let arrayToNumber = Number([]);
console.log(typeof arrayToNumber, arrayToNumber); // number 0

Number conversion এ উপরের ইনপুট ছাড়া আমরা যে কোন টাইপের ডাটাই ইনপুট দেই না কেন, তার অধিকাংশ আউটপুট আসবে NaN ( Not a Number ) , যেমন →

let invalidNumberString = Number("1234abc");
console.log(typeof invalidNumberString, invalidNumberString); // number NaN

let validStringNumber = Number("abc");
console.log(typeof validStringNumber, validStringNumber); // number NaN

let NumberNaN = Number(NaN);
console.log(typeof NumberNaN, NumberNaN); // number NaN

let NumberUndefined = Number(undefined);
console.log(typeof NumberUndefined, NumberUndefined); // number NaN

NaN দেখাবে। কারন ( 'abc' , NaN , undefined ) কোন্‌ ভালিড নাম্বার নয়। এখন প্রশ্ন হচ্ছে যে, তাহলে পূর্বের কোডে যখন আমরা ( true , false & null ) রে কনভার্ট করলাম তখন এর আউটপুট কেন ( 1 , 0 & 0 ) আসল?

console.log(true == 1); // true
console.log(false == 0); //true

true আর false এর Number Conversion ( 1 & 0 ) হল। আর এটা আমাদের মনে রাখতে হবে যে null কে নাম্বারে কনভার্ট করলে 0 হয়।

console.log(Number(Symbol('1234')))    // TypeError is thrown

Explicit Conversion to Boolean - 

let number0 = Boolean(0);
console.log(typeof number0, number0); // boolean false

let stringLength_0 = Boolean("");
console.log(typeof stringLength_0, stringLength_0); // boolean false

let booleanNull = Boolean(null);
console.log(typeof booleanNull, booleanNull); // boolean false

let booleanNaN = Boolean(NaN);
console.log(typeof booleanNaN, booleanNaN); // boolean false

let booleanUndefined = Boolean(undefined);
console.log(typeof booleanUndefined, booleanUndefined); // boolean false

let booleanFalse = Boolean(false);
console.log(typeof booleanFalse, booleanFalse); // boolean false

let bigIntToBoolean = Boolean(0n);
console.log(typeof bigIntToBoolean, bigIntToBoolean); // boolean false

উপরের এই সাতটি ক্ষেত্র ব্যতিত সকল কনভার্সানে Boolean আমাদের true আউটপুট দিবে। সব থেকে সহজ সূত্র।

Explicit Conversion to BigInt -

BigInt সব কিছুর Conversion করতে পারে না, String, Number & Boolean→← BigInt কনভার্ট করা যায়।

let stringToBigInt = BigInt("1234");
console.log(typeof stringToBigInt, stringToBigInt); // bigint 1234n

let slashBigInt = BigInt("\n");
console.log(typeof slashBigInt, slashBigInt); // bigint 0n

let trimBigInt = BigInt("   1234   ");
console.log(typeof trimBigInt, trimBigInt); // bigint 1234n

let numberToBigInt = BigInt(1234);
console.log(typeof numberToBigInt, numberToBigInt); // bigint 1234n

let booleanTrueToBigInt = BigInt(true);
console.log(typeof booleanTrueToBigInt, booleanTrueToBigInt); // bigint 1n

let booleanFalseToBigInt = BigInt(false);
console.log(typeof booleanFalseToBigInt, booleanFalseToBigInt); // bigint 1n

Number এবং BigInt অনেকটাই একি ভাবে কনভার্সন ঘটায়। শুধু পার্থক্য হল যে BigInt কোন দশমিক নাম্বার গ্রহন ও করে না আবার কনভার্ট ও করে না।

Implicit Conversion

Implicit coercion এর বিষয়ে এখন কিছু আমরা জানি তবে এখন আমরা এই কনভার্সন নিয়ে বিস্তারিত আলোচনা করব -

যেহেতু আমদের Explicit coercion এর ধারনা আছে সেহেতু আমাদের Implicit coercion বুঝতে সহজ হবে।

Implicit Coercion মূলত ঘটে কিছু operator এর মাধ্যমে। আর এই অপেরটরকে মুলত দুই ভাগে ভাগ করা হয় -

  1. Numeric Conversion Operator.
  2. Loosely Equality Operator.

Numeric Conversion Operator -

<, <=, >=, >, |, &, ~, ^, -, *, /, %  +  এইসব অপারেটর সব সময় ভালিড নাম্বার স্ট্রিংকে নাম্বারে কনভার্ট করবে শুধু + অপারেটরটা একটু আলাদা। কারন এই  +  অপারেটর দুই ধরনের হয় -

  1. Binary + Operator.
  2. Unary + Operator.
  • যখন দুইটা অপারেন্ড থাকবে আর তার মাঝখানে + অপারেটর থাকবে তখন সেই অপারেটরকে বাইনারি + অপেরাটর বলে। বাইনারি + অপারেটরের বৈশিষ্ট হল, যদি দুইটা অপারেন্ডের মাঝে একটি স্ট্রিং থাকে তাহলে তা স্ট্রিংএ কনভার্ট হবে। আর তা সব সমায় বাম থেকে ডান দিকে কাউন্ট করে।
console.log('abcd' + 1234); // abcd1234
console.log(10 + 5 + 'abcd' + 1234); // 15abcd1234

এখান থেকে বুঝা যায় যে বাইনারি অপারেটর কিভাবে কাজ করছে।

  • ইউনারি + অপারেটর একটি অপারেন্ডের সামনে বসে এবং তাকে নাম্বারে কনবার্ট করে। আর তা যদি ভালিড নাম্বার ডিজিট না হয় তাইলে NaN রেটার্ন করবে।
console.log(+'100'); // 100
console.log(+true); // 1
console.log(+false); // 0
console.log(+null); // 0

condole.log(+1n); // Uncaught TypeError: Cannot convert a BigInt value to a number

console.log(+'abcd'); // NaN
console.log(+'abcd1234'); // NaN
console.log(+undefined); // NaN

Implicit coercion এর মাধ্যমে BigInt কে নাম্বারে কনভার্ট করা যায় না। এর জন্য Explicit coercion ই করতে হয়।

+ অপারেটর ছাড়া নিউমেরিক কানভার্সনের যে অপারেটরই ব্যবহার করা হক না কেন্‌ তা নাম্বারেই কনভার্ট হয়। আর যদি ভালিড্‌ নাম্বার বা ডিজিট না হয় তাইলে NaN রিটার্ন করে -

console.log('100' > 50);
// Number('100') > 50
// 100 > 50
// true
console.log('100' / 50);
// Number('100') / 50
// 100 / 50
// 2
console.log('10' * true);
// Number('10') * 1
// 10 * 1
// 10
console.log('10' * false);
// Number('10') * 0
// 10 * 0
// 0

console.log('abcd' * 1234); // NaN

// < , <= , > , >=
console.log(10 < "15"); // true
console.log(10 <= "15"); // true
console.log(10 <= "15abc"); // false
console.log("30" > "10"); // true
console.log("10" >= "10"); // true
console.log("30" >= "10abc"); // true

// ~
console.log(~"1234"); // -1234
console.log(~"-1234"); // 1234
console.log(~"abcd1234"); //-1

এইভাবে সব কিছুকেই নাম্বারে কনভার্ট করে।

Loosely Equality Operator -

এই অপারেটর দুইটা ==, != । এই অপারেটরও নিউমেরিক কনভার্সন ঘটায় তবে যদি দুই অপারেন্ড স্ট্রিং হয় তাহলে সে নিউমেরিক কনভার্সন করবে না তখন সে লেক্সিকোগ্রাফিকাল সিস্টেম ব্যবহার করে স্ট্রিং এর মধ্যে কম্পেয়ার করে একটি বুলিয়ান ভালু রিটার্ন করে।

console.log(10 == '10');
// 10 == Number('10')
// 10 == 10
// true

console.log(10 != '20')
// 10 != Number('20')
// 10 != 20
// true

console.log('1234' == '1234'); // true use lexicographical system

console.log(1234 == 'abcd'); // false "use lexicographical system"
console.log('abcd' == 'ABCD') // false "use lexicographical system"
console.log('abcd' == 'abcd') // true "use lexicographical system"

// Because from lexicographical system 'a' > 'A'
console.log('a' > 'A'); // true

// NaN not equal to anything else
console.log(NaN == NaN); // false

এখন কিছু উদাহরণের মধ্যমে ইমপ্লিছিট কনভারসান দেখা যাক →

true + true = 2
// 1 + 1
// 2
true + false = 1
// 1 + 0
// 1

এখানে কিন্তু Behind the scenes Explicit Coercion ই ঘটতেছে যা আমরা উপরে দেখে আসছি। এখানে + অপারেটরটা নিউমেরিক কনভার্সন ঘটাচ্ছে। আর বুলিয়ান ভালুকে নাম্বারে কনভার্ট করতেছে।

এখন আমরা কোন্‌ অব্জেক্ট এর কনভার্সান করি তাইলে কি হয় দেখা যাক →

{} + null + [1]
// '[object object]null1'

আমরা এক্সপ্লিছিট কনভার্সন থেকে জানি যে, কোন অব্জেক্টকে যখন স্ট্রিংয়ে কনভার্ট করা হয় তখন তা আমাদের '[object object]' রিটার্ন করে। আর এখানে প্রথমে যেহেতু অব্জেক্ট আর তার পর বাইনারি + অপারেটর দেওয়া আছে তাই সেটা সম্পূর্ন স্টেটমেন্টটাকে স্ট্রিং এ রুপান্তর করে ফেলছে।

{} + + null + [1]
// '[object Object]01'

এখানে অব্জেক্টের পরে দুইটা + অপারেটর থাকার কারনে প্রথমটা বাইনারি এবং দ্বিতীয়টা উনারি অপারেটর হিসেবে কনভার্ট হচ্ছে তাই আমরা এবার আউটপুটে স্ট্রিংয়ের ভিতরে 'null' এর জায়গায় '0' পাচ্ছি। কেন পাচ্ছি কারন null এর নিউমেরিক কনভার্সন হচ্ছে 0

null < 1 // true
null > 0 // false

এখানে উক্ত null নাম্বারে কনভার্ট হয়ে 0 হয়ে যাচ্ছে।

! Operator Coercion

! + [] + {} + ![] + null
// ! +Number([]) + String({}) + !Boolean([]) + String(null)
// !0 + '[Object Object]' + false + 'null'
// 'true[Object Object]falsenull'

মনে রাখতে হবে যে ! অপারেটর যদি কোন একটি অপারেন্ডের সামনে বসে ইউনারির মত তাহলে সে উক্ত অপারেন্ডকে বুলিয়ানে কনভার্ট করবে। তবে উদাহরনের প্রথমের ! অপারেটর কিন্তু পরে বুলিয়ান কনভার্সান ঘটাইছে সে আরেকে প্রথমে নাম্বারে কনভার্ট করেছে কারন ঐখানে + অপারেটরটা ইউনারি অপারেটর হিসেবে কনভার্সন ঘটাইছে। কারন ! কোন অপারেন্ড নয়, এটি একটি অপারেটর।

শেষ কথা -

অভিন্দন 🎉🎉, আপনি এখন জাভাস্ক্রিপ্টের টাইপ কোরসান নিয়ে যথেষ্ট জেনে গিয়েছেন, আর আপনার জাভাস্ক্রিপ্টের আজব ছিন্টাক্স গুল বুজতে কষ্ট হবে না বলে আমি আসা করি। আর এর ব্যবহার যে কত ব্যাপক তা আপনি এখন বুঝতে পারেবেন। এখন বেশি বেশি প্রাক্টিছের মাধ্যমে একে আয়ত্ত করতে হবে নাইলে এই আর্টিকেল আপনার কোন কাজেই আসবে না। তাই প্রাক্টিছের মাধ্যমে নিজের হাত কে নোংরা করার অনুরোধ রইল। Happy Coding 💻