টাইপস্ক্রিপ্টের শক্তিশালী ম্যাপড টাইপস এবং কন্ডিশনাল টাইপস-এর একটি সম্পূর্ণ নির্দেশিকা, যেখানে মজবুত ও টাইপ-সেফ অ্যাপ্লিকেশন তৈরির জন্য ব্যবহারিক উদাহরণ এবং উন্নত ব্যবহারের ক্ষেত্র অন্তর্ভুক্ত রয়েছে।
টাইপস্ক্রিপ্টের ম্যাপড টাইপস এবং কন্ডিশনাল টাইপস আয়ত্ত করা
টাইপস্ক্রিপ্ট, যা জাভাস্ক্রিপ্টের একটি সুপারসেট, মজবুত এবং রক্ষণাবেক্ষণযোগ্য অ্যাপ্লিকেশন তৈরির জন্য শক্তিশালী ফিচার প্রদান করে। এই ফিচারগুলোর মধ্যে, ম্যাপড টাইপস এবং কন্ডিশনাল টাইপস উন্নত টাইপ ম্যানিপুলেশনের জন্য অপরিহার্য টুল হিসেবে পরিচিত। এই গাইডটি এই ধারণাগুলির একটি সম্পূর্ণ বিবরণ প্রদান করে, তাদের সিনট্যাক্স, ব্যবহারিক প্রয়োগ এবং উন্নত ব্যবহারের ক্ষেত্রগুলি অন্বেষণ করে। আপনি একজন অভিজ্ঞ টাইপস্ক্রিপ্ট ডেভেলপার হোন বা আপনার যাত্রা সবে শুরু করেছেন, এই নিবন্ধটি আপনাকে এই ফিচারগুলি কার্যকরভাবে ব্যবহার করার জ্ঞানে সজ্জিত করবে।
ম্যাপড টাইপস কী?
ম্যাপড টাইপস আপনাকে বিদ্যমান টাইপগুলিকে রূপান্তরিত করে নতুন টাইপ তৈরি করার সুযোগ দেয়। এটি একটি বিদ্যমান টাইপের প্রপার্টিগুলির উপর ইটারেট করে এবং প্রতিটি প্রপার্টিতে একটি রূপান্তর প্রয়োগ করে। এটি বিশেষত বিদ্যমান টাইপের বিভিন্ন সংস্করণ তৈরি করার জন্য দরকারী, যেমন সমস্ত প্রপার্টিকে ঐচ্ছিক বা শুধুমাত্র পঠনযোগ্য (read-only) করা।
বেসিক সিনট্যাক্স
একটি ম্যাপড টাইপের সিনট্যাক্স নিম্নরূপ:
type NewType<T> = {
[K in keyof T]: Transformation;
};
T
: যে ইনপুট টাইপটির উপর আপনি ম্যাপ করতে চান।K in keyof T
: ইনপুট টাইপT
-এর প্রতিটি কী-এর উপর ইটারেট করে।keyof T
T
-এর সমস্ত প্রপার্টির নামের একটি ইউনিয়ন তৈরি করে, এবংK
ইটারেশনের সময় প্রতিটি স্বতন্ত্র কী-কে প্রতিনিধিত্ব করে।Transformation
: প্রতিটি প্রপার্টিতে আপনি যে রূপান্তর প্রয়োগ করতে চান। এটি একটি মডিফায়ার (যেমনreadonly
বা?
) যোগ করা, টাইপ পরিবর্তন করা, বা সম্পূর্ণ অন্য কিছু হতে পারে।
ব্যবহারিক উদাহরণ
প্রপার্টিগুলিকে শুধুমাত্র পঠনযোগ্য (Read-Only) করা
ধরা যাক, আপনার একটি ইন্টারফেস আছে যা একটি ব্যবহারকারীর প্রোফাইল উপস্থাপন করে:
interface UserProfile {
name: string;
age: number;
email: string;
}
আপনি একটি নতুন টাইপ তৈরি করতে পারেন যেখানে সমস্ত প্রপার্টি শুধুমাত্র পঠনযোগ্য:
type ReadOnlyUserProfile = {
readonly [K in keyof UserProfile]: UserProfile[K];
};
এখন, ReadOnlyUserProfile
-এর প্রপার্টিগুলি UserProfile
-এর মতোই থাকবে, কিন্তু সেগুলি সবই শুধুমাত্র পঠনযোগ্য হবে।
প্রপার্টিগুলিকে ঐচ্ছিক (Optional) করা
একইভাবে, আপনি সমস্ত প্রপার্টিকে ঐচ্ছিক করতে পারেন:
type OptionalUserProfile = {
[K in keyof UserProfile]?: UserProfile[K];
};
OptionalUserProfile
-এ UserProfile
-এর সমস্ত প্রপার্টি থাকবে, কিন্তু প্রতিটি প্রপার্টি ঐচ্ছিক হবে।
প্রপার্টির টাইপ পরিবর্তন করা
আপনি প্রতিটি প্রপার্টির টাইপও পরিবর্তন করতে পারেন। উদাহরণস্বরূপ, আপনি সমস্ত প্রপার্টিকে স্ট্রিং-এ রূপান্তর করতে পারেন:
type StringifiedUserProfile = {
[K in keyof UserProfile]: string;
};
এই ক্ষেত্রে, StringifiedUserProfile
-এর সমস্ত প্রপার্টি string
টাইপের হবে।
কন্ডিশনাল টাইপস কী?
কন্ডিশনাল টাইপস আপনাকে এমন টাইপ সংজ্ঞায়িত করতে দেয় যা একটি শর্তের উপর নির্ভর করে। এটি একটি টাইপ কোনো নির্দিষ্ট সীমাবদ্ধতা পূরণ করে কিনা তার উপর ভিত্তি করে টাইপের সম্পর্ক প্রকাশ করার একটি উপায় প্রদান করে। এটি জাভাস্ক্রিপ্টের টারনারি অপারেটরের মতো, কিন্তু এটি টাইপের জন্য।
বেসিক সিনট্যাক্স
একটি কন্ডিশনাল টাইপের সিনট্যাক্স নিম্নরূপ:
T extends U ? X : Y
T
: যে টাইপটি পরীক্ষা করা হচ্ছে।U
: যে টাইপটিT
দ্বারা এক্সটেন্ড করা হচ্ছে (শর্ত)।X
: যদিT
,U
-কে এক্সটেন্ড করে (শর্তটি সত্য হয়) তবে যে টাইপটি রিটার্ন করা হবে।Y
: যদিT
,U
-কে এক্সটেন্ড না করে (শর্তটি মিথ্যা হয়) তবে যে টাইপটি রিটার্ন করা হবে।
ব্যবহারিক উদাহরণ
একটি টাইপ স্ট্রিং কিনা তা নির্ধারণ করা
আসুন একটি টাইপ তৈরি করি যা ইনপুট টাইপটি স্ট্রিং হলে string
রিটার্ন করে, এবং অন্যথায় number
রিটার্ন করে:
type StringOrNumber<T> = T extends string ? string : number;
type Result1 = StringOrNumber<string>; // string
type Result2 = StringOrNumber<number>; // number
type Result3 = StringOrNumber<boolean>; // number
একটি ইউনিয়ন থেকে টাইপ এক্সট্র্যাক্ট করা
আপনি একটি ইউনিয়ন টাইপ থেকে একটি নির্দিষ্ট টাইপ এক্সট্র্যাক্ট করতে কন্ডিশনাল টাইপ ব্যবহার করতে পারেন। উদাহরণস্বরূপ, নন-নালেবল টাইপ এক্সট্র্যাক্ট করতে:
type NonNullable<T> = T extends null | undefined ? never : T;
type Result4 = NonNullable<string | null | undefined>; // string
এখানে, যদি T
null
বা undefined
হয়, টাইপটি never
হয়ে যায়, যা পরে টাইপস্ক্রিপ্টের ইউনিয়ন টাইপ সরলীকরণ দ্বারা ফিল্টার হয়ে যায়।
টাইপ ইনফার করা
কন্ডিশনাল টাইপগুলি infer
কীওয়ার্ড ব্যবহার করে টাইপ ইনফার করার জন্যও ব্যবহার করা যেতে পারে। এটি আপনাকে আরও জটিল টাইপ কাঠামো থেকে একটি টাইপ এক্সট্র্যাক্ট করতে দেয়।
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
function myFunction(x: number): string {
return x.toString();
}
type Result5 = ReturnType<typeof myFunction>; // string
এই উদাহরণে, ReturnType
একটি ফাংশনের রিটার্ন টাইপ এক্সট্র্যাক্ট করে। এটি পরীক্ষা করে যে T
এমন একটি ফাংশন কিনা যা কোনো আর্গুমেন্ট নেয় এবং একটি টাইপ R
রিটার্ন করে। যদি তাই হয়, এটি R
রিটার্ন করে; অন্যথায়, এটি any
রিটার্ন করে।
ম্যাপড টাইপস এবং কন্ডিশনাল টাইপস একত্রিত করা
ম্যাপড টাইপস এবং কন্ডিশনাল টাইপসের আসল শক্তি আসে তাদের একত্রিত করার মাধ্যমে। এটি আপনাকে অত্যন্ত নমনীয় এবং অভিব্যক্তিপূর্ণ টাইপ রূপান্তর তৈরি করতে দেয়।
উদাহরণ: ডিপ রিডঅনলি (Deep Readonly)
একটি সাধারণ ব্যবহারের ক্ষেত্র হলো এমন একটি টাইপ তৈরি করা যা একটি অবজেক্টের সমস্ত প্রপার্টি, নেস্টেড প্রপার্টিসহ, শুধুমাত্র পঠনযোগ্য করে তোলে। এটি একটি রিকার্সিভ কন্ডিশনাল টাইপ ব্যবহার করে করা যেতে পারে।
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};
interface Company {
name: string;
address: {
street: string;
city: string;
};
}
type ReadonlyCompany = DeepReadonly<Company>;
এখানে, DeepReadonly
রিকার্সিভভাবে সমস্ত প্রপার্টি এবং তাদের নেস্টেড প্রপার্টিগুলিতে readonly
মডিফায়ার প্রয়োগ করে। যদি একটি প্রপার্টি একটি অবজেক্ট হয়, তবে এটি সেই অবজেক্টের উপর রিকার্সিভভাবে DeepReadonly
কল করে। অন্যথায়, এটি কেবল প্রপার্টিতে readonly
মডিফায়ার প্রয়োগ করে।
উদাহরণ: টাইপ দ্বারা প্রপার্টি ফিল্টার করা
ধরা যাক আপনি এমন একটি টাইপ তৈরি করতে চান যা কেবল একটি নির্দিষ্ট টাইপের প্রপার্টি অন্তর্ভুক্ত করে। এটি অর্জন করতে আপনি ম্যাপড টাইপস এবং কন্ডিশনাল টাইপস একত্রিত করতে পারেন।
type FilterByType<T, U> = {
[K in keyof T as T[K] extends U ? K : never]: T[K];
};
interface Person {
name: string;
age: number;
isEmployed: boolean;
}
type StringProperties = FilterByType<Person, string>; // { name: string; }
type NonStringProperties = Omit<Person, keyof StringProperties>;
এই উদাহরণে, FilterByType
T
-এর প্রপার্টিগুলির উপর ইটারেট করে এবং প্রতিটি প্রপার্টির টাইপ U
-কে এক্সটেন্ড করে কিনা তা পরীক্ষা করে। যদি করে, তবে এটি ফলাফল টাইপে প্রপার্টিটি অন্তর্ভুক্ত করে; অন্যথায়, এটি কী-কে never
-এ ম্যাপ করে এটিকে বাদ দেয়। কী রিম্যাপ করার জন্য "as" এর ব্যবহার লক্ষ্য করুন। তারপরে আমরা মূল ইন্টারফেস থেকে স্ট্রিং প্রপার্টিগুলি সরানোর জন্য `Omit` এবং `keyof StringProperties` ব্যবহার করি।
উন্নত ব্যবহারের ক্ষেত্র এবং প্যাটার্ন
বেসিক উদাহরণগুলির বাইরে, ম্যাপড টাইপস এবং কন্ডিশনাল টাইপস আরও উন্নত পরিস্থিতিতে অত্যন্ত কাস্টমাইজযোগ্য এবং টাইপ-সেফ অ্যাপ্লিকেশন তৈরি করতে ব্যবহার করা যেতে পারে।
ডিস্ট্রিবিউটিভ কন্ডিশনাল টাইপস
কন্ডিশনাল টাইপগুলি ডিস্ট্রিবিউটিভ হয় যখন পরীক্ষিত টাইপটি একটি ইউনিয়ন টাইপ হয়। এর মানে হল যে শর্তটি ইউনিয়নের প্রতিটি সদস্যের উপর পৃথকভাবে প্রয়োগ করা হয়, এবং ফলাফলগুলি তখন একটি নতুন ইউনিয়ন টাইপে একত্রিত হয়।
type ToArray<T> = T extends any ? T[] : never;
type Result6 = ToArray<string | number>; // string[] | number[]
এই উদাহরণে, ToArray
ইউনিয়ন string | number
-এর প্রতিটি সদস্যের উপর পৃথকভাবে প্রয়োগ করা হয়েছে, যার ফলে string[] | number[]
হয়েছে। যদি শর্তটি ডিস্ট্রিবিউটিভ না হত, তবে ফলাফলটি (string | number)[]
হত।
ইউটিলিটি টাইপস ব্যবহার করা
টাইপস্ক্রিপ্ট বেশ কিছু বিল্ট-ইন ইউটিলিটি টাইপ সরবরাহ করে যা ম্যাপড টাইপস এবং কন্ডিশনাল টাইপসের উপর ভিত্তি করে তৈরি। এই ইউটিলিটি টাইপগুলি আরও জটিল টাইপ রূপান্তরের জন্য বিল্ডিং ব্লক হিসাবে ব্যবহার করা যেতে পারে।
Partial<T>
:T
-এর সমস্ত প্রপার্টিকে ঐচ্ছিক করে তোলে।Required<T>
:T
-এর সমস্ত প্রপার্টিকে আবশ্যক করে তোলে।Readonly<T>
:T
-এর সমস্ত প্রপার্টিকে শুধুমাত্র পঠনযোগ্য করে তোলে।Pick<T, K>
:T
থেকেK
প্রপার্টিগুলির একটি সেট নির্বাচন করে।Omit<T, K>
:T
থেকেK
প্রপার্টিগুলির একটি সেট সরিয়ে দেয়।Record<K, T>
:T
টাইপেরK
প্রপার্টিগুলির একটি সেট দিয়ে একটি টাইপ তৈরি করে।Exclude<T, U>
:T
থেকে সেই সমস্ত টাইপ বাদ দেয় যাU
-তে অ্যাসাইন করা যায়।Extract<T, U>
:T
থেকে সেই সমস্ত টাইপ এক্সট্র্যাক্ট করে যাU
-তে অ্যাসাইন করা যায়।NonNullable<T>
:T
থেকেnull
এবংundefined
বাদ দেয়।Parameters<T>
: একটি ফাংশন টাইপT
-এর প্যারামিটারগুলি গ্রহণ করে।ReturnType<T>
: একটি ফাংশন টাইপT
-এর রিটার্ন টাইপ গ্রহণ করে।InstanceType<T>
: একটি কনস্ট্রাক্টর ফাংশন টাইপT
-এর ইনস্ট্যান্স টাইপ গ্রহণ করে।
এই ইউটিলিটি টাইপগুলি শক্তিশালী টুল যা জটিল টাইপ ম্যানিপুলেশনকে সহজ করতে পারে। উদাহরণস্বরূপ, আপনি Pick
এবং Partial
একত্রিত করে এমন একটি টাইপ তৈরি করতে পারেন যা শুধুমাত্র নির্দিষ্ট কিছু প্রপার্টিকে ঐচ্ছিক করে তোলে:
type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
interface Product {
id: number;
name: string;
price: number;
description: string;
}
type OptionalDescriptionProduct = Optional<Product, "description">;
এই উদাহরণে, OptionalDescriptionProduct
-এ Product
-এর সমস্ত প্রপার্টি রয়েছে, কিন্তু description
প্রপার্টিটি ঐচ্ছিক।
টেমপ্লেট লিটারেল টাইপস ব্যবহার করা
টেমপ্লেট লিটারেল টাইপস আপনাকে স্ট্রিং লিটারেলের উপর ভিত্তি করে টাইপ তৈরি করতে দেয়। ডাইনামিক এবং অভিব্যক্তিপূর্ণ টাইপ রূপান্তর তৈরি করতে এগুলি ম্যাপড টাইপস এবং কন্ডিশনাল টাইপসের সাথে একত্রে ব্যবহার করা যেতে পারে। উদাহরণস্বরূপ, আপনি এমন একটি টাইপ তৈরি করতে পারেন যা সমস্ত প্রপার্টির নামের আগে একটি নির্দিষ্ট স্ট্রিং যুক্ত করে:
type Prefix<T, P extends string> = {
[K in keyof T as `${P}${string & K}`]: T[K];
};
interface Settings {
apiUrl: string;
timeout: number;
}
type PrefixedSettings = Prefix<Settings, "data_">;
এই উদাহরণে, PrefixedSettings
-এর প্রপার্টিগুলি হবে data_apiUrl
এবং data_timeout
।
সেরা অনুশীলন এবং বিবেচ্য বিষয়
- সহজ রাখুন: যদিও ম্যাপড টাইপস এবং কন্ডিশনাল টাইপস শক্তিশালী, তবে এগুলি আপনার কোডকে আরও জটিল করে তুলতে পারে। আপনার টাইপ রূপান্তরগুলিকে যতটা সম্ভব সহজ রাখার চেষ্টা করুন।
- ইউটিলিটি টাইপস ব্যবহার করুন: যখনই সম্ভব টাইপস্ক্রিপ্টের বিল্ট-ইন ইউটিলিটি টাইপস ব্যবহার করুন। সেগুলি ভালোভাবে পরীক্ষিত এবং আপনার কোডকে সহজ করতে পারে।
- আপনার টাইপগুলি ডকুমেন্ট করুন: আপনার টাইপ রূপান্তরগুলি স্পষ্টভাবে ডকুমেন্ট করুন, বিশেষ করে যদি সেগুলি জটিল হয়। এটি অন্যান্য ডেভেলপারদের আপনার কোড বুঝতে সাহায্য করবে।
- আপনার টাইপগুলি পরীক্ষা করুন: আপনার টাইপ রূপান্তরগুলি প্রত্যাশিতভাবে কাজ করছে কিনা তা নিশ্চিত করতে টাইপস্ক্রিপ্টের টাইপ চেকিং ব্যবহার করুন। আপনি আপনার টাইপের আচরণ যাচাই করতে ইউনিট টেস্ট লিখতে পারেন।
- পারফরম্যান্স বিবেচনা করুন: জটিল টাইপ রূপান্তরগুলি আপনার টাইপস্ক্রিপ্ট কম্পাইলারের পারফরম্যান্সকে প্রভাবিত করতে পারে। আপনার টাইপের জটিলতা সম্পর্কে সচেতন থাকুন এবং অপ্রয়োজনীয় গণনা এড়িয়ে চলুন।
উপসংহার
ম্যাপড টাইপস এবং কন্ডিশনাল টাইপস টাইপস্ক্রিপ্টের শক্তিশালী ফিচার যা আপনাকে অত্যন্ত নমনীয় এবং অভিব্যক্তিপূর্ণ টাইপ রূপান্তর তৈরি করতে সক্ষম করে। এই ধারণাগুলি আয়ত্ত করার মাধ্যমে, আপনি আপনার টাইপস্ক্রিপ্ট অ্যাপ্লিকেশনগুলির টাইপ সেফটি, রক্ষণাবেক্ষণযোগ্যতা এবং সামগ্রিক গুণমান উন্নত করতে পারেন। প্রপার্টিগুলিকে ঐচ্ছিক বা শুধুমাত্র পঠনযোগ্য করার মতো সাধারণ রূপান্তর থেকে শুরু করে জটিল রিকার্সিভ রূপান্তর এবং কন্ডিশনাল লজিক পর্যন্ত, এই ফিচারগুলি আপনাকে মজবুত এবং স্কেলেবল অ্যাপ্লিকেশন তৈরির জন্য প্রয়োজনীয় টুল সরবরাহ করে। এদের সম্পূর্ণ সম্ভাবনা উন্মোচন করতে এবং আরও দক্ষ টাইপস্ক্রিপ্ট ডেভেলপার হতে এই ফিচারগুলি অন্বেষণ এবং পরীক্ষা চালিয়ে যান।
আপনার টাইপস্ক্রিপ্ট যাত্রা চালিয়ে যাওয়ার সময়, অফিসিয়াল টাইপস্ক্রিপ্ট ডকুমেন্টেশন, অনলাইন কমিউনিটি এবং ওপেন-সোর্স প্রকল্প সহ উপলব্ধ প্রচুর রিসোর্স ব্যবহার করার কথা মনে রাখবেন। ম্যাপড টাইপস এবং কন্ডিশনাল টাইপসের শক্তিকে আলিঙ্গন করুন, এবং আপনি সবচেয়ে চ্যালেঞ্জিং টাইপ-সম্পর্কিত সমস্যাগুলি মোকাবেলা করার জন্য সুসজ্জিত হবেন।