Typescript: أضف واجهات دعم لتحديد الأساليب الثابتة

تم إنشاؤها على ١٣ يناير ٢٠١٧  ·  43تعليقات  ·  مصدر: microsoft/TypeScript

إصدار TypeScript: 2.0.3

الشفرة

interface Foo {
public static myStaticMethod(param1: any);
}

سلوك متوقع:
لا اخطاء
السلوك الفعلي:
غير مدعوم

Question

التعليق الأكثر فائدة

aluanhaddad أجد الكود الخاص بك حلاً

interface JsonSerializable {
      public static fromJson(obj: any);
      public toJson(): string;
}

أيهما تراه بوضوح أكثر؟

ال 43 كومينتر

ماذا تريد أن تحقق مع هذا؟ هل يمكنك توضيح السيناريو؟

أتخيل أن فئات abstract هي ما تبحث عنه.

تضمين التغريدة
مثال:

interface JsonSerializable {
      public static fromJson(obj: any);
      public toJson(): string;
}

rozzzly في هذا المثال ، abstract class غير صالح.

أعتقد أن هذا سيكون رائعًا! أضافت Java هذه الوظيفة في الإصدار الأخير.

تضمين التغريدة
أعتقد أنك قد تجد هذا مثيرًا للاهتمام:

interface JsonSerializableStatic<C extends new (...args) => JsonSerializable<C>> {
  fromJson(json: string): JsonSerializable<C>;
}

interface JsonSerializable<C extends new (...args) => any> {
  toJson: () => string;
  constructor: C;
}

interface A extends JsonSerializable<typeof A> { }
class A implements JsonSerializable<typeof A> {

  constructor(readonly id: number, readonly name: string) { }
  toJson() { return JSON.stringify(this); }

  static fromJson(json: string): A {
    const obj = JSON.parse(json);
    return new A(obj.id, obj.name);
  }
}

const a = new A(1, 'Charlize');

const json = a.toJson();

const y = A.fromJson(json);
console.info(a, json, y);
console.info(new a.constructor(1, 'Theron'));
const m = new A.prototype.constructor(1, 'Charlize Theron');
console.info(m);

aluanhaddad أجد الكود الخاص بك حلاً

interface JsonSerializable {
      public static fromJson(obj: any);
      public toJson(): string;
}

أيهما تراه بوضوح أكثر؟

aluanhaddad أجد الكود الخاص بك حلاً

واجهة JsonSerializable {
ثابت عام من Json (obj: أي) ؛
public toJson (): سلسلة ؛
}
أيهما تراه بوضوح أكثر؟

في الواقع ، لكن لي ليس حلاً ، إنه تبرير لحالة استخدامك. تنفيذ إلغاء التسلسل الثابت وتسلسل المثيل على أساس فئة بفئة وبالتالي تغليفه وآمنه.
تصريحك:

interface JsonSerializable {
     public static fromJson(obj: any);
     public toJson(): string;
}

غير ذي صلة لأنه يعيد تعريف واجهة كائن JSON. هذا بالتأكيد لا يتطلب أساليب واجهة ثابتة بأي شكل من الأشكال.

أنت تفقد مفهوم الواجهة. يجب أن يجعلني class A implements JsonSerializable أنفذ كلتا الطريقتين. لكن في الواقع يجعلني أنفذ:

toJson: () => string;
constructor: new (...args) => JsonSerializableStatic<C>;

إنه ليس حلاً واضحًا.

لا يوجد سبب تقني لعدم السماح بتعريف الأساليب الثابتة على الواجهات.

Serginho أنا لا أجادل في أن الوضع مثالي. كنت أحاول فقط توضيح أنه يمكن التعبير عنها.

aluanhaddad تعال! افتح عقلك. هل تعتقد أن الكتابة المطبوعة يجب أن تسمح بالطرق الثابتة في الواجهات؟ تم تنفيذ هذا في Java 8 في الإصدار الأخير ، لذلك أعتقد أنني لا أتحدث عن هراء.

Serginho لا أعتقد أنه مناسب بشكل خاص لـ TypeScript. يجب أن تحدد الواجهات الوظيفة التي يوفرها الكائن. يجب أن تكون هذه الوظيفة قابلة للتجاوز وقابلة للتبديل (لهذا السبب تكون طرق الواجهة افتراضية). الإحصائيات هي مفهوم مواز للسلوك الديناميكي / الأساليب الافتراضية. لا أشعر أن التشابك بين الاثنين صحيح من وجهة نظر التصميم.

كما كتب aluanhaddad بالفعل ، لدى TypeScript بالفعل آلية للتعبير عما تريده. الاختلاف الكبير هو أنك في هذه الحالة تتعامل مع الفصل ككائن ، والذي يستمر في الاتساق المنطقي. من وجهة نظري ، النهج الذي تقترحه ليس مناسبًا بشكل خاص لـ TypeScript (وتطوير JavaScript) لأن الفئات هي نوع من الاختراق / مواطنين من الدرجة الثانية. تعتمد الأنماط السائدة حاليًا على برمجة التنميط / الشكل ، وليس على الفئات الجامدة.

أعتقد أنك قد تفتح عقلك أيضًا وتجرب أنماطًا مختلفة من البرمجة. العالم لا يبدأ ولا ينتهي بـ OOP. قد تجد البرمجة ذات الوظائف والأشياء العادية و (وإلى حد ما) النماذج الأولية ممتعة بل وأفضل. يتماشى هذا النمط بشكل أكبر مع ما تم تصميم JavaScript من أجله في البداية. في ملاحظة جانبية ، أوافق على أن JS يتحول ببطء نحو أنماط OOP-esque (على الأقل على مستوى اللجنة) ، ولكن هذا بسبب الدفع الهائل للأشخاص الذين لا يشعرون بالارتياح مع نماذج البرمجة المختلفة وتقنيات التطوير. بالنسبة لي ، إنه أمر محزن ومحبط للغاية.

كما قال gcnew ، تصف الواجهة شكل كائن فردي ، وليس

import assert = require("assert");

interface JsonSerializableStatic<JsonType, InstanceType extends JsonSerializable<JsonType>> {
    fromJson(obj: JsonType): InstanceType;
}
interface JsonSerializable<JsonType> {
    toJson(): JsonType;
}

interface PointJson { x: number; y: number; }
class Point /*static implements JsonSerializableStatic<PointJson, Point>*/ {
    static fromJson(obj: PointJson): Point {
        return new Point(obj.x, obj.y)
    }

    constructor(readonly x: number, readonly y: number) {}

    toJson(): PointJson {
        return { x: this.x, y: this.y };
    }
}
// Hack for 'static implements'
const _: JsonSerializableStatic<PointJson, Point> = Point;

function testSerialization<JsonType, InstanceType extends JsonSerializable<JsonType>>(cls: JsonSerializableStatic<JsonType, InstanceType>, json: JsonType) {
    const instance: InstanceType = cls.fromJson(json);
    const outJson: JsonType = instance.toJson();
    assert.deepEqual(json, outJson);
}
testSerialization(Point, { x: 1, y: 2 });

يؤدي الإخفاق في تلبية توقيع إما toJson أو fromJson إلى حدوث خطأ في التجميع. (لسوء الحظ ، الخطأ هو const _ بدلاً من الطريقة.)

من المحتمل أن تكون مرتبطة (لأنها تتعامل مع كتابة طرق ثابتة): # 5863.


aluanhaddad : هناك خطأ في المثال الخاص بك: JsonSerializable 's constructor يشير العضو في الواقع إلى خاصية مثيل تسمى constructor ، والتي عند استدعائها بـ new إرجاع JsonSerializableStatic . ما يعنيه ذلك هو أن (new ((new X()).constructor)).fromJson({}) يجب أن يعمل. السبب في نجاح عملية التحويل هو أن interface A extends JsonSerializable<typeof A> يعلن أن التطبيق صالح بدون التحقق منه فعليًا. على سبيل المثال ، هذا يجمع بدون أخطاء:

interface I { m(): void; }
interface A extends I { }
// No compile error
class A implements I { em() {} }

Serginho ليس مستخدم Java ، ولكن لا يبدو أن اللغة تسمح لك بتحديد واجهة للجانب الثابت للفئة (بمعنى أن الفئات التي تنفذ الواجهة يجب أن تنفذ طريقة ثابتة لتتوافق). تسمح لك Java بتعريف طريقة ثابتة بجسم في واجهة ، يكون مكافئ TypeScript منها:

interface I { ... }
namespace I {
   export function interfaceStaticMethod() {}
}

ماذا لو كنت تحاول إنشاء مصنع عام؟

interface Factorizable {
  static factory<U>(str: string): U
}

class Foo {
  private data: string[] = []
  bar<T extends Factorizable>(): T[] {
    return this.data.map(T.factory);
  }
}

class Bar implements Factorizable {
  static factory(str: string): Bar {
    // ...
  }
}

// Usage
var x = new Foo();
var y: Bar[] = x.bar();

لست متأكدًا من بناء الجملة interface الذي أقترحه هنا ولكني أعرف أن صيغة "الاستخدام" هي ما أحاول تحقيقه. هذا نمط أستخدمه كثيرًا في Swift (باستخدام protocols ) وأعتقد أنه سيكون رائعًا حقًا في TypeScript. على الرغم من أنني لست مصمم لغة أو منفذ مترجم ، فأنا لست متأكدًا مما إذا كان يناسب الاتجاه المقصود من TypeScript أم أنه واقعي ليتم تنفيذه.

لا يحتاج الفصل إلى implement للواجهة. ما عليك سوى تحديد الواجهة ، وستعمل عمليات فحص النوع الهيكلي على تخزين أية مشكلات في موقع الاستخدام مؤقتًا ، على سبيل المثال:

interface Factorizable<U> {
    factory(str: string): U
}

class Foo {
  private data: string[] = []
  bar<T>(factory: Factorizable<T>): T[] {
    return this.data.map(factory.factory);
  }
}

class Bar {
  static factory(str: string): Bar {
    // ...
  }
}

// Usage
var x = new Foo();
var y = x.bar(Bar); // Bar[]

mhegazy هذا حل جيد نسبيًا. شكرا لك على توفير ذلك! 🙏

لا يزال هناك شيئان يسببان لي الانزعاج (من المسلم به أنهما لا يوقفان العرض):

  1. ما زلنا غير قادرين على التصريح بأن Bar ينفذ صراحةً أو يتوافق مع Factorizable .

    • من الناحية العملية ، أتخيل أن هذه ليست مشكلة حقًا. نظرًا لأنه سيكون صحيحًا أنه إذا تغيرت واجهة Factorizable بطريقة غير متوافقة ، فإن الاستخدام x.bar(Bar) سيبدأ في الخطأ ، ثم تقوم بتصحيح تغييرات الانجراف.
  2. بالنسبة لي ، لا يزال من العبء المعرفي إعلان نوع y على الجانب الأيمن من المهمة.

    • لا تزال هذه البنية أكثر غرابة من شأنها تمكين هذا السلوك: var y: Baz[] = x.bar(Bar) . من الواضح أنه خطأ ، ولكن بناء الجملة يمكّن المطور من تقييد المشكلة بشكل مفرط من خلال تحديد نوع الإرجاع في مكانين.

ما زلنا غير قادرين على التصريح بأن Bar ينفذ بشكل صريح أو يتوافق مع Factorizable.

هناك نوعان متضمنان ، 1. وظيفة المُنشئ (على سبيل المثال ، الجانب الثابت من الفئة) ، و 2. جانب المثيل (ما يظهر عند استدعاء new ). خلط هذين النوعين في نوع واحد غير صحيح. من الناحية النظرية ، يمكن أن يكون لديك implements و static implements ولكن من الناحية العملية ، كما لاحظت ، نادرًا ما يتم استخدام هذا ، ولا تضيف عبارة الأدوات الكثير. يتم التحقق في موقع الاستخدام بأي طريقة بغض النظر عما إذا كان لديك عبارة implements أم لا.

بالنسبة لي ، لا يزال من العبء المعرفي إعلان نوع y على الجانب الأيمن من المهمة.

هذان يعنيان مختلفين ، يعلن var y = x.bar(Bar) عن متغير جديد y من نفس النوع مثل x.bar(Bar) ؛ حيث و var y: Bar[] = x.bar(Bar) التصريح عن متغير جديد y مع نوع Bar[] ويتحقق أن هذا النوع من x.bar(Bar) هو تنازل إلى Bar[] .

وجود سياد هو بالأحرى مسألة أسلوب. أنا شخصياً توصيتي بعدم استخدام كتابة التعليقات التوضيحية بشكل صريح ما لم تضطر إلى ذلك ؛ دع الأنواع تتدفق من خلال النظام. لقد رأيت قواعد التعليمات البرمجية ، مع ذلك ، حيث يكون دليل النمط هو عكس ذلك ، حيث يحتوي كل شيء على تعليق توضيحي واضح من النوع.

mhegazy أشكركم على المناقشة / المنظور.

@ andy-hanson شكرًا على الوقت الذي أمضيته في تصحيح الأخطاء. أصلحت الخطأ في المثال الخاص بي.

يعمل هذا أيضًا ، ويظهر الخطأ في وقت الترجمة دون أي استدعاء وظيفة إضافية:

interface Type<T> {
    new (...args: any[]): T;
}

/* static interface declaration */
interface ComparableStatic<T> extends Type<Comparable<T>> {
    compare(a: T, b: T): number;
}

/* interface declaration */
interface Comparable<T> {
    compare(a: T): number;
}

/* class decorator */
function staticImplements<T>() {
    return (constructor: T) => {}
}

@staticImplements<ComparableStatic<TableCell>>()   /* this statement implements both normal interface & static interface */
class TableCell { /* implements Comparable<TableCell> { */  /* not required. become optional */
    value: number;

    compare(a: TableCell): number {
        return this.value - a.value;
    }

    static compare(a: TableCell, b: TableCell): number {
        return a.value - b.value;
    }
}

ما هي نتيجة المناقشة؟

واجهت هذا وأريد أيضًا استخدام static interface :

interface IDb {
  public static instance: () => Db,
}

ينسى معظم الناس أن هناك بالفعل واجهات _static_ بمعنى أن وظيفة / فئة المُنشئ لها واجهتان بالفعل ، واجهة المُنشئ وواجهة المثيل.

interface MyFoo {
  method(): void;
}

interface MyFooConstructor {
  new (): MyFoo;
  prototype: MyFoo;
  staticMethod(): any;
}

const MyFoo = function MyFoo() {
  this.prop = '';
} as any as MyFooConstructor;

MyFoo.prototype = {
  method() { console.log(this); }
}

MyFoo.staticMethod = function () { /* do something static */ }

إذا لم تكن تستخدم فئات _abstract_ ، فأنت تمتلك القوة بالفعل.

شكرا @ kitsonk على الرد.

يبدو أن تصريحك يجب أن يعمل ، لكنه مطول جدًا بالنسبة للحالة.

لقد جربت للتو فئات _abstract_ ، لكن يبدو أنها لا تدعم static مع abstract .

[ts] 'static' modifier cannot be used with 'abstract' modifier.

zixia هذا هو الإصدار رقم 14600

نعم ، دعنا نصوت عليه.

هل يهتم أي شخص بالرد مع حل لسؤالي هنا: http://stackoverflow.com/questions/44047874/dynamically-modify-typescript-classes-through-a-generic-function

أعتقد أنه سيتم حل المشكلة برمتها من خلال القدرة على تحديد أعضاء ثابتة على واجهات ، وإذا تم إغلاق هذه المشكلة لأنها ليست ضرورية ، أود أن أرى كيفية حلها بدلاً من ذلك.

grantila لقد أجبت على سؤالك. كما ذكرنا سابقًا في هذه المشكلة ، ما لم تكن لديك متطلبات أخرى غير مذكورة هناك ، يمكن حل هذا بسهولة عن طريق التعامل مع الفئات ككائنات.

@ Enet4 لقد قمت بتحديث السؤال ، لقد كان مبسطًا للغاية. لا يمكن إصلاح المشكلة الحقيقية عن طريق اختراق Object.defineProperty() للأسف. الذي راجع للشغل ، هو اختراق. أريد التأكد من عدم وجود خطأ إملائي في make - فحص ثابت مناسب بشكل أساسي.

هذه مشكلة حقيقية لدي ، الكود الذي بدأت في نقله إلى TypeScript ولكني احتفظت به الآن كـ JavaScript ، حيث لا يمكنني حاليًا إعادة كتابة مثل هذه الكميات الهائلة من التعليمات البرمجية كما كان من الضروري خلاف ذلك.

أريد التأكد من عدم وجود أخطاء إملائية - فحص ثابت مناسب بشكل أساسي.

يمكن أن يذهب الفحص الثابت فقط هنا حتى الآن. ولكن إذا كان شاغلك الوحيد الآن هو أنك تقوم بتعيين الملكية الصحيحة ، فإن الإرسال إلى نوع صانع وتعيين الخاصية من هناك يبدو أنه يعالج ذلك.

@ Enet4 هذا حل عملي ، شكرًا. أعتقد أنه يجب إعادة النظر في هذه المشكلة (13462) ، حيث إن الحلول المشابهة لك ، باستخدام الكتابة بالنوع ، هي في الواقع ليست آمنة من النوع ، وإذا كانت هذه هي الطريقة الوحيدة لحل حالة العمل مع نوع الفئة كقيمة ، فإننا تفقد الكثير من مرونة اللغة الديناميكية.

الحلول المشابهة لك ، باستخدام أسلوب الكتابة ، هي في الواقع ليست آمنة للكتابة ، وإذا كانت هذه هي الطريقة الوحيدة لحل موقف العمل مع نوع الفصل كقيمة ، فإننا نفقد الكثير من مرونة اللغة الديناميكية.

grantila في دفاعي ، هذا قابل للنقاش. : wink: حالة الاستخدام الخاصة بك مختلفة عن تلك المعروضة في هذه المشكلة ، من حيث أن نوع الفصل الدراسي الخاص بك قد (أو لا يوفر) طريقة اعتمادًا على ظروف وقت التشغيل. و IMO هو أكثر خطورة من نوع المصبوب المقدم في إجابتي ، والذي تم إجراؤه فقط للسماح بإدخال حقل في كائن. بهذا المعنى ، يجب أن يظل نوع الفصل الناتج C & Maker<T> متوافقًا مع كل شيء آخر يعتمد على C .

حاولت أيضًا تصوير الأماكن التي يمكن أن تساعدك فيها الأساليب الثابتة في الواجهات هنا ، لكن ربما أفتقد شيئًا ما. حتى لو كان لديك شيء مثل static make?(... args: any[]): self في واجهتك ، فسيتعين عليك التحقق منه في وقت التشغيل للوجود قبل المكالمة. إذا كنت ترغب في مواصلة هذه المناقشة ، فلنفكر في القيام بذلك في مكان آخر لتقليل الضوضاء. : قليلا_ابتسامة_الوجه:

إذن لا يمكننا كتابة طرق التحقق من المصنع الثابت في الفئات التي تنفذ نفس الواجهة؟

حالة الاستخدام الخاصة بي هي:

interface IObject {
    static make(s: string): IObject;
}

class A implements IObject{
    static make(s: string): IObject {
        // Implementation A...
    }
}

class B implements IObject{
    static make(s: string): IObject {
        // Implementation B...
    }
}

A.make("string"); // returns A
B.make("string"); // returns B

لا أريد أن أكتب فئة مصنع جديدة لهذا الغرض فقط.

@ tyteen4a03 قم بإزالة IObject من هذا المثال ، وسيتم تجميعه. راجع أيضًا https://github.com/Microsoft/TypeScript/issues/17545#issuecomment -319422545

@ andy-ms نعم من الواضح أنه يعمل ولكن الهدف الكامل من فحص النوع هو .. فحص الأنواع. يمكنك دائمًا التقليل من أمان النوع بدرجة كافية لجعل كل حالة استخدام مجمعة ، ولكن هذا يتجاهل حقيقة أن هذا طلب ميزة وغير مجنون في ذلك.

نعم من الواضح أنه يعمل ولكن الهدف الكامل من فحص النوع هو .. فحص الأنواع.

هذا خيط طويل طويل يوضح كيف أن الجانب الثابت للفئة هو واجهة منفصلة لجانب المثيل و implements يشير إلى جانب المثيل. @ andy-ms كان يشير إلى @ tyteen4a03 كيفية الحصول على قصاصة من التعليمات البرمجية ، لأنه كان _ خاطئًا_ ، وليس التخلي عن فحص النوع.

حالة الاستخدام الخاصة بي للسماح للطرق الثابتة باستخدام المعلمة العامة للفئة هي لفئات mixin. أقوم ببناء إطار عمل للكيان يستخدم التعليقات التوضيحية لتحديد الأعمدة وقواعد البيانات وما إلى ذلك ، وأود أن يكون هناك وظيفة ثابتة مختلطة مع فئات الكيان الخاص بي والتي من شأنها أن تسمح بالوصول المريح إلى المستودع المكتوب بشكل صحيح.

class RepositoryMixin<T> {
    public static repository(): EntityRepository<T> {
        return new EntityRepository<T>(Object.getPrototypeOf(this));
    }
}

@mixin(RepositoryMixin)
class Entity implements RepositoryMixin<Entity> {
    public id: number;
}

Entity.repository().save(new Entity());

rmblstrp هل يمكنك إظهار كيف يمكنك استخدام الميزة المقترحة في

rmblstrp الذي لا يتطلب هذه الميزة. في الواقع ، نظرًا لأنك تستخدم مصممًا ، يمكنك في الواقع _its type_ للتحقق من أن الفئات المشروحة توفر طرقًا ثابتة مطلوبة. لا تحتاج إلى كليهما وسيكون في الواقع زائدة عن الحاجة.

مرحبًا ، لا أريد أن أكون خارج الموضوع أو خارج نطاق هذه المحادثة.
ومع ذلك ، نظرًا لأنك طرحت مناقشة نماذج برمجة مختلفة (هل يجب أن نستخدم OOP أم وظيفية؟) ، فأنا أريد أن أتحدث على وجه التحديد عن المصانع الثابتة التي تُستخدم عادةً لإنشاء اتصال بـ db أو تقديم نوع من الخدمة.
في العديد من اللغات مثل PHP و Java ، كانت المصانع الثابتة مثل الإهمال لصالح حقن التبعية. أصبحت حاويات Dependency Injection و IOC شائعة بفضل أطر مثل Symfony و Spring.
تحتوي الكتابة النصية على حاوية IOC رائعة تسمى InversifyJS.

هذه هي الملفات حيث يمكنك أن ترى كيف يتم التعامل مع كل شيء.
https://github.com/Deviad/virtual-life/blob/master/models/generic.ts
https://github.com/Deviad/virtual-life/blob/master/service/user.ts
https://github.com/Deviad/virtual-life/blob/master/models/user.ts
https://github.com/Deviad/virtual-life/blob/master/utils/sqldb/client.ts
https://github.com/Deviad/virtual-life/blob/master/bootstrap.ts

أنا لا أقول إنه حل مثالي (وربما يترك بعض السيناريوهات مكشوفة) ولكنه يعمل أيضًا مع React ، فهناك بعض الأمثلة التي يمكنك الاطلاع عليها بالفعل.

أيضًا ، أقترح عليك إلقاء نظرة على هذا الفيديو حول البرمجة الوظيفية التي تغطي هذا الجانب حول أيهما أفضل: https://www.youtube.com/watch؟v=e-5obm1G_FY&t=1487s
المفسد: لا أحد أفضل ، هذا يعتمد على المشكلة التي تريد حلها. إذا كنت تتعامل مع المستخدمين ، والفصول الدراسية ، فسيكون من الأفضل للمدرسين OOP أن يصمموا مشكلتك باستخدام الكائنات.
إذا كنت بحاجة إلى محلل يقوم بمسح موقع ويب ، فربما يكون من الأفضل استخدام مولدات الوظائف التي تعرض نتائج جزئية ، وما إلى ذلك :)

aluanhaddadDeviad منذ منصبي لقد تم استخدام InversifyJS وأنها كانت قطعا كبيرة وبالتأكيد الكثير أفضل طريقة للذهاب. في وقت رسالتي ، كنت قد بدأت للتو في استخدام Typescript / Node بعد أن كنت PHP / C # سابقًا. لقد استغرق الأمر القليل من الوقت للتعرف على البيئة والحزم المتاحة.

ما هي حالة هذا؟ لماذا تستمر في إغلاق المشكلات التي لم يتم حلها في الريبو؟

أعتقد أن هذه إحدى تلك الحالات التي يجب فيها تسمية المشكلة صراحةً بـ "wontfix" ، لأن اختيار عدم وجود طرق ثابتة في الواجهات يكون حسب التصميم.

@ enet4 ، أنا وافد جديد ، لكن هذا لم يكن واضحًا على الإطلاق. عند قراءة هذا وغيره من القضايا ذات الصلة ، يبدو أنه في الغالب القضايا التالية:

A. إنه صعب
ب. لا نحب (كل) بناء جملة محدد رأيناه حتى الآن.
ج- لا تعتقد أقلية صغيرة أنه يجب أن يكون ممكنًا على الإطلاق ، ويفضلون حذف الطريقة الغريبة الحالية للقيام بذلك.

إذا كان في الواقع عن طريق التصميم ، والمسؤولون لا يريدون ذلك ، فيجب عليهم كتابة مستند عام ، وربطه في هذا الموضوع وغيره من المواضيع. سيوفر لنا هذا الكثير من الوقت لإبقائنا في طي النسيان.

لقد ربطنا بالفعل بـ # 14600 في هذا الموضوع وهذه هي المشكلة التي يجب اتباعها لطلب الميزة.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات