TypeScript: فئة TypeScript (class)
آخر تحديث: 2026-08-26
تُعد «الفئات» إحدى ميزات بناء الجملة الموجهة للكائنات التي أُضيفت في لغة جافا سكريبت ES6. وتستند لغة تايب سكريبت إلى هذه الميزة من خلال إضافة تعليقات الأنواع والتحكم في الوصول — مما يضمن أن خصائص الفئات وأساليبها تخضع لقيود الأنواع.
1. القواعد الأساسية لبناء الفئات
(1) إعلانات المتغيرات والمنشئات
يتطلب TypeScript الإعلان عن خصائص الفئة قبل استخدامها — على عكس JavaScript:
class User {
// Property Declaration(TypeScript Requirements)
name: string;
age: number;
// Constructor
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// Methods
greet(): string {
return `Hello,I am${this.name},This year${this.age} years old`;
}
}
let user = new User("Charlie", 20);
console.log(user.greet()); // "Hello,I amCharlie,This year20 years old"
(2) طرق متعددة لتهيئة الخصائص
class Config {
// Method 1:Initialize directly upon declaration
host: string = "localhost";
port: number = 3000;
// Method 2:Initialization in the Constructor
env: string;
constructor(env: string) {
this.env = env;
}
// Method 3:Assert that it is not empty(!)——You promise to assign a value later.
data!: string;
}
let cfg = new Config("development");
cfg.data = "some data"; // Create First, Then Assign
!، ولكن تأكد من تعيين قيمة لهذه الخاصية فعليًّا.
2. مُعدِّلات الوصول
تتحكم مُعدِّلات الوصول الثلاثة في TypeScript في مدى ظهور الخصائص والطرق:
| المُعدِّل | داخل الفئة | الفئة الفرعية | خارج الفئة |
|---|---|---|---|
public |
✅ | ✅ | ✅ |
protected |
✅ | ✅ | ❌ |
private |
✅ | ❌ | ❌ |
(1) عام (الافتراضي)
class Person {
public name: string; // public This is the default value.,Optional
public age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
let p = new Person("Charlie", 20);
console.log(p.name); // ✅ Accessible from outside the class
p.name = "Diana"; // ✅ Can be modified from outside the class
(2) خاص
class BankAccount {
private balance: number;
constructor(initialBalance: number) {
this.balance = initialBalance;
}
public deposit(amount: number): void {
if (amount > 0) this.balance += amount;
}
public withdraw(amount: number): boolean {
if (amount > 0 && amount <= this.balance) {
this.balance -= amount;
return true;
}
return false;
}
public getBalance(): number {
return this.balance;
}
}
let account = new BankAccount(1000);
account.deposit(500);
account.withdraw(200);
console.log(account.getBalance()); // 1300
// account.balance; // ❌ private The property is not accessible from outside
// account.balance = 99999; // ❌ private Properties cannot be modified externally
(3) محمي
class Animal {
protected name: string;
constructor(name: string) {
this.name = name;
}
protected makeSound(sound: string): void {
console.log(`${this.name}:${sound}`);
}
}
class Dog extends Animal {
constructor(name: string) {
super(name);
}
public bark(): void {
this.makeSound("Woof!"); // ✅ Subclasses can access protected Methods
// console.log(this.name); // ✅ Subclasses can access protected Properties
}
}
let dog = new Dog("Wangcai");
dog.bark(); // "Wangcai:Woof!"
// dog.name; // ❌ protected The property is not accessible from outside
// dog.makeSound("Howl"); // ❌ protected This method cannot be called from outside the class.
(4) الاختصار في معلمات المنشئ
يوفر TypeScript خصائص المعلمات — فمن خلال إضافة مُعدِّلات قبل معلمات مُنشئ الدالة، يقوم تلقائيًّا بإعلان الخصائص وتهيئتها:
// Complete Syntax
class User1 {
public name: string;
private age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
// Abbreviation——The effect is exactly the same
class User2 {
constructor(
public name: string,
private age: number
) {}
}
let u = new User2("Charlie", 20);
console.log(u.name); // ✅ public
// console.log(u.age); // ❌ private
3. المُعدِّل «readonly»
readonly لا يمكن تعيين قيم للخصائص التي تحتوي على مُعدِّلات إلا عند الإعلان عنها أو في مُنشئ الكائن:
class Circle {
readonly radius: number;
constructor(radius: number) {
this.radius = radius; // ✅ You can assign values in the constructor
}
get area(): number {
return Math.PI * this.radius ** 2;
}
}
let circle = new Circle(5);
console.log(circle.area); // 78.54
// circle.radius = 10; // ❌ readonly Properties cannot be modified
(1) خصائص "للقراءة فقط" + خصائص المعلمات
class Config {
constructor(
readonly host: string,
readonly port: number
) {}
}
let cfg = new Config("localhost", 3000);
console.log(`${cfg.host}:${cfg.port}`); // "localhost:3000"
// cfg.host = "other"; // ❌ readonly
4. أدوات الوصول (أدوات الاسترجاع / أدوات التعيين)
تتيح لك أدوات الوصول تنفيذ منطق مخصص عند قراءة الخصائص أو كتابتها:
(1) قواعد النحو الأساسية
class Employee {
private _salary: number = 0;
// getter——Execute on read
get salary(): number {
return this._salary;
}
// setter——Perform validation during assignment
set salary(value: number) {
if (value < 0) {
throw new Error("Salaries cannot be negative.");
}
this._salary = value;
}
}
let emp = new Employee();
emp.salary = 8000; // Call setter
console.log(emp.salary); // Call getter → 8000
// emp.salary = -100; // ❌ Throw an error
(2) الخصائص المخصصة للقراءة فقط (التي تحتوي على دالة استرجاع دون دالة تعيين)
class User {
constructor(
private firstName: string,
private lastName: string
) {}
get fullName(): string {
return `${this.firstName} ${this.lastName}`;
}
// None setter → fullName It is read-only.
}
let user = new User("San", "Zhang");
console.log(user.fullName); // "San Zhang"
// user.fullName = "Si Li"; // ❌ None setter
▶ مثال: محول درجات الحرارة
class Temperature {
private _celsius: number = 0;
constructor(celsius: number) {
this._celsius = celsius;
}
get celsius(): number {
return this._celsius;
}
set celsius(value: number) {
if (value < -273.15) {
throw new Error("The temperature must not fall below absolute zero.(-273.15°C)");
}
this._celsius = value;
}
get fahrenheit(): number {
return this._celsius * 9 / 5 + 32;
}
set fahrenheit(value: number) {
this.celsius = (value - 32) * 5 / 9; // Reuse celsius Verification of
}
}
let temp = new Temperature(100);
console.log(`${temp.celsius}°C = ${temp.fahrenheit}°F`); // "100°C = 212°F"
temp.fahrenheit = 32;
console.log(`${temp.celsius}°C = ${temp.fahrenheit}°F`); // "0°C = 32°F"
الناتج:
100°C = 212°F
0°C = 32°F
5. العناصر الثابتة
static الخصائص والطرق المعدلة تنتمي إلى الفئة نفسها، وليس إلى المثيل:
(1) الخصائص والطرق الثابتة
class MathUtils {
static PI: number = 3.14159;
static circleArea(radius: number): number {
return MathUtils.PI * radius ** 2;
}
static clamp(value: number, min: number, max: number): number {
return Math.min(Math.max(value, min), max);
}
}
// No need to create an instance,Call directly using the class name
console.log(MathUtils.PI); // 3.14159
console.log(MathUtils.circleArea(5)); // 78.53975
console.log(MathUtils.clamp(150, 0, 100)); // 100
(2) الكتل الثابتة (TypeScript 5.0 / ES2022+)
class Config {
static host: string;
static port: number;
// Static Initialization Block
static {
Config.host = process.env.HOST ?? "localhost";
Config.port = Number(process.env.PORT) || 3000;
}
}
(3) الفرق بين العناصر الثابتة وعناصر المثيل
class Counter {
static totalCount: number = 0; // Shared by all instances
instanceCount: number = 0; // Each instance is independent
increment(): void {
Counter.totalCount++;
this.instanceCount++;
}
}
let c1 = new Counter();
let c2 = new Counter();
c1.increment();
c1.increment();
c2.increment();
console.log(`Examples1:${c1.instanceCount}`); // 2
console.log(`Examples2:${c2.instanceCount}`); // 1
console.log(`Total:${Counter.totalCount}`); // 3
▶ مثال: فئة مع معدّلات الوصول واختصار المُنشئ
الناتج:
100C = 212F
0C = 32F
class Article {
constructor(
readonly id: number,
public title: string,
private _views: number = 0
) {}
get views(): number {
return this._views;
}
incrementViews(): void {
this._views++;
}
toString(): string {
return `[${this.id}] ${this.title} (${this.views} views)`;
}
}
let post = new Article(1, "Hello TypeScript");
post.incrementViews();
post.incrementViews();
console.log(post.toString()); // "[1] Hello TypeScript (2 views)"
// post._views; // ❌ private — cannot access from outside
الناتج:
100C = 212F
0C = 32F
▶ مثال: طرق المصنع الثابتة والخصائص للقراءة فقط
الناتج:
100C = 212F
0C = 32F
class Point {
constructor(
public readonly x: number,
public readonly y: number
) {}
static origin: Point = new Point(0, 0);
static fromAngle(angle: number, distance: number): Point {
return new Point(
Math.round(distance * Math.cos(angle)),
Math.round(distance * Math.sin(angle))
);
}
distanceTo(other: Point): number {
return Math.sqrt((this.x - other.x) ** 2 + (this.y - other.y) ** 2);
}
}
let p = Point.fromAngle(Math.PI / 4, 10);
console.log(`(${p.x}, ${p.y})`); // "(7, 7)"
console.log(p.distanceTo(Point.origin)); // "~10"
الناتج:
100C = 212F
0C = 32F
❓ أسئلة شائعة
private خاص حقًّا أثناء وقت التشغيل؟private في TypeScript ما هو إلا فحص يتم في وقت التحويل البرمجي — فلا يزال من الممكن الوصول إلى الخصائص الخاصة في كود JavaScript المُحوَّل. إن بناء الجملة #private في ES2022 (الحقول الخاصة) هو ما يوفر الخصوصية الحقيقية أثناء وقت التشغيل. كما يدعم TypeScript #private: class C { #secret = 1; }.private، ومتى يجب استخدام protected؟private لتفاصيل التنفيذ الداخلية التي «تحتاجها الفئة نفسها فقط»؛ بينما تُستخدم protected للأعضاء التي «قد تحتاج الفئات الفرعية أيضًا إلى الوصول إليها أو تجاوزها». إذا كنت غير متأكد، فابدأ بـ private — يمكنك دائمًا تغييرها إلى protected لاحقًا إذا لزم الأمر (فمن الأكثر أمانًا أن تكون أكثر تساهلاً بدلاً من أن تكون أكثر تقييدًا).this في الطرق الثابتة؟this refers to the فئة itself (not an مثيل). In static create() { return new this(); }, this is equal to the current فئة. However, this may be lost in arrow functions or callbacks—it is recommended to use the فئة name instead of this.📖 ملخص
- في TypeScript، يتعين إعلان الخصائص في الفئات قبل استخدامها؛ ويمكن تهيئة الخصائص عند الإعلان عنها، أو تعيين قيم لها في المنشئ، أو
!التحقق من صحتها. - مُعدِّلات الوصول: public (الافتراضي، يمكن الوصول إليها من خارج الفئة)، protected (يمكن الوصول إليها من قبل الفئات الفرعية)، private (لا يمكن الوصول إليها إلا داخل الفئة)
- تعمل خصائص المعلمات على تبسيط صيغة إعلانات الخصائص وعمليات التعيين في منشئ الدالة
- لا يمكن تهيئة الخصائص المخصصة للقراءة فقط إلا عند الإعلان عنها أو في منشئ الكائن
- تُنفِّذ طرق الاسترجاع/التعيين منطقًا مخصصًا عند قراءة خاصية ما أو كتابتها؛ وإذا كانت هناك طريقة استرجاع دون طريقة تعيين، فإن الخاصية تكون للقراءة فقط.
- العناصر الثابتة تنتمي إلى الفئة نفسها، وليس إلى المثيلات؛ ويتم الوصول إليها عبر
ClassName.member
📝 تمارين
- المشكلة الأساسية (صعوبة ⭐): أنشئ فئة
Rectangle(تحتوي على خصائص العرض والارتفاع) وقم بتنفيذ الطريقتينgetArea()وgetPerimeter(). قم بتبسيط منشئ الفئة باستخدام الخصائص المعلمة. - مشكلة متقدمة (درجة الصعوبة ⭐⭐): أنشئ فئة
BankAccount— اجعلbalanceخاصًّا، واسمح بإجراء العمليات عبر طرقdepositوwithdraw، واجعل الرصيد متاحًا للقراءة فقط باستخدام دالة getter، وتأكد من كفاية الرصيد قبل إجراء عمليةwithdraw. - التحدي (الصعوبة: ⭐⭐⭐): قم بتنفيذ فئة عامة
Stack<T>— قم بتخزين البيانات في مصفوفة خاصة، ووفر طرق دفع و pop و peek و size، وقم بتنفيذ الخاصية size (للقراءة فقط) باستخدام طرق الحصول (getters)، واستخدم طريقة ثابتةstatic fromArray<T>(items: T[]): Stack<T>لإنشاء مكدس من المصفوفة.