Typescript: السماح بالفهرسة بالرموز

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

يحتوي TypeScript الآن على وضع هدف ES6 والذي يتضمن التعريفات Symbol . ومع ذلك ، عند محاولة فهرسة كائن برمز ، أحصل على خطأ (يجب أن تكون وسيطة تعبير الفهرس من النوع "سلسلة" أو "رقم" أو "أي").

var theAnswer = Symbol('secret');
var obj = {};
obj[theAnswer] = 42; // Currently error, but should be allowed
Moderate Fix Available Suggestion help wanted

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

تم التعرّض للعض من هذا الإصدار 3.0.1.
أريد سجلاً يقبل symbol لكن TS لن يسمح لي بذلك.

لقد مرت 3.5 سنوات منذ فتح هذا الإصدار ، هل يمكننا الحصول على رموز الآن ، من فضلك 🙏

المفارقة هي أن TS تناقض نفسها.
يقوم TS بتوسيع keyof any = number | string | symbol .

ولكن بعد ذلك عندما تفعل record[symbol] ترفض TS القول
لا يمكن استخدام _Type "رمز" كمفهرس_.

ال 93 كومينتر

هذا جزء من دعم رمز ES6 الذي نعمل عليه

wereHamster ، مع طلب السحب # 1978 ، يجب أن يصبح هذا قانونيًا ، وسيكون obj[theAnswer] النوع any . هل هذا كافٍ لما تبحث عنه ، أم أنك بحاجة إلى كتابة أقوى؟

هل يمكن تحديد نوع الخصائص المفهرسة بالرموز؟ شيء من هذا القبيل:

var theAnswer = Symbol('secret');
interface DeepThought {
   [theAnswer]: number;
}

بناءً على التعليقات الواردة في تلك العلاقات العامة ، لا:

_لا يشمل ذلك مفهرسات الرموز ، والتي تسمح لكائن ما بالعمل كخريطة بمفاتيح رموز عشوائية.

أعتقد أن wereHamster يتحدث عن كتابة أقوى من @ dquirk. هناك 3 مستويات للدعم هنا. يتم توفير المستوى الأساسي من خلال العلاقات العامة الخاصة بي ، ولكن هذا مخصص فقط للرموز التي تمثل خصائص كائن الرمز العام ، وليس الرموز المحددة من قبل المستخدم. وبالتالي،

var theAnswer = Symbol('secret');
interface DeepThought {
    [Symbol.toStringTag](): string; // Allowed
    [theAnswer]: number; // not allowed
}

سيكون المستوى التالي من الدعم هو السماح لمفهرس الرموز:

var theAnswer = Symbol('secret');
interface DeepThought {
   [s: symbol]: number;
}
var d: DeepThought;
d[theAnswer] = 42; // Typed as number

هذا على رادارنا ، ويمكن تنفيذه بسهولة.

أقوى مستوى هو ما تطلبه ، وهو شيء مثل:

var theAnswer = Symbol('secret');
var theQuestion = Symbol('secret');
interface DeepThought {
   [theQuestion]: string;
   [theAnswer]: number;
}
var d: DeepThought;
d[theQuesiton] = "why";
d[theAnswer] = 42;

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

مع العلاقات العامة الخاصة بي ، يجب أن تكون قادرًا على الأقل على استخدام رمز لسحب قيمة _out_ من كائن. سيكون any ، لكنك لن تحصل على خطأ بعد الآن.

wereHamster لقد

لقد قمت بدمج الطلب رقم 1978 ، لكنني سأترك هذا الخطأ مفتوحًا ، حيث يبدو أنه يطلب أكثر مما قدمته مع هذا التغيير. ومع ذلك ، مع التغيير الذي أجريته ، سيختفي الخطأ الأصلي.

wereHamster هل يمكنك نشر تحديث لما تود رؤيته يحدث هنا أكثر من ذلك؟ لم يتضح لي على الفور ما نفذناه مقابل ما نشرته

هل لديك أي فكرة عندما يكون symbol نوعًا صالحًا كمفهرس؟ هل هذا شيء يمكن القيام به كمجتمع علاقات عامة؟

سنأخذ العلاقات العامة لهذا. يمكن لـ JsonFreeman تقديم تفاصيل حول بعض المشكلات التي قد تواجهك.

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

RyanCavanaugh ، سيكون من الجيد أن يكون لديك في النهاية المثال الأخير في https://github.com/Microsoft/TypeScript/issues/1863#issuecomment -73668456 فحص الكتابة. ولكن إذا كنت تفضل ذلك ، يمكنك تقسيم هذه المشكلة إلى عدة مشكلات أصغر يتم إنشاؤها فوق بعضها البعض.

هل كان هناك أي تحديث على هذه الجبهة؟ AFAIU أحدث إصدار من المترجم يدعم فقط المستوى الأول الموضح في https://github.com/Microsoft/TypeScript/issues/1863#issuecomment -73668456.

سنكون سعداء لقبول PRs لهذا التغيير.

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

يتم تعقب التتبع المستمر بالفعل في https://github.com/Microsoft/TypeScript/issues/5579. تتعلق هذه المشكلة بإضافة دعم لمفهرس الرموز ، على غرار مفهرسات السلسلة والرقم.

فهمت ، من المنطقي.

JsonFreemanmhegazy قضية متاح في # 12932

فقط ظننت أنني سألقي بعلبة الاستخدام في الحلبة. أنا أكتب أداة تسمح بوصف الاستعلامات من خلال تحديد مفاتيح النص العادي للمطابقة مع خصائص الكائن العشوائية ، والرموز لتحديد عوامل التشغيل المطابقة. باستخدام الرموز لمشغلين معروفين ، أتجنب الغموض في مطابقة عامل مقابل حقل يكون مفتاحه هو نفسه مفتاح المشغل المعروف.

نظرًا لأنه لا يمكن تحديد الرموز كمفاتيح فهرس ، على عكس ما تسمح به JavaScript صراحةً ، فأنا مجبر على الإرسال إلى <any> في عدد من الأماكن ، مما يقلل من جودة الشفرة.

interface Query {
  [key: string|symbol]: any;
}

const Q = {
  startsWith: Symbol('startsWith'),
  gte: Symbol('gte'),
  lte: Symbol('lte')
}

const sample: Query = {
  name: {
    [Q.startsWith]: 'M',
    length: {
      [Q.lte]: 25
    }
  },
  age: {
    [Q.gte]: 18
  }
};

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

اهلا ياجماعة. هل هناك أي حركة في هذا؟ أحتاجه لذلك سأكون سعيدًا للمساهمة في التغييرات اللازمة. لم تساهم في TS من قبل.

mhegazyRyanCavanaugh أعلم أنكم مشغولون بشكل لا يصدق ، ولكن هل يمكن أن تزنوا عندما تسنحكم الفرصة؟ تعتبر الرموز أداة مهمة حقًا لتصميم المكتبات والأطر ، كما أن الافتقار إلى القدرة على استخدامها في الواجهات يعد نقطة ضعف محددة.

أسأل هل هناك أي شيء قيد التقدم؟ نأمل بصدق أن يتم دعم هذه الميزة.

نعم ، ما زلت أبحث عن هذا اليوم ، هذا ما أراه في Webstorm:

screenshot 2017-10-08 21 37 17

هذا في الواقع يعمل

var test: symbol = Symbol();

const x = {
    [test]: 1
};

x[test];

console.log(x[test]);

console.log(x['test']);

لكن نوع x ليس صحيحًا ، حيث يُستدل على أنه

{
  [key: string]: number
}

نعم ، ما زلت أبحث عن هذا اليوم ، هذا ما أراه في Webstorm:

يرجى ملاحظة أن خدمة اللغة الخاصة بـ JetBrains يتم تمكينها افتراضيًا في WebStorm و intelliJ IDEA وما إلى ذلك.

يعمل هذا في TS 2.7

const key = Symbol('key')
const a: { [key]?: number } = {}
a[key] = 5

أي تحديث على هذا؟

مشكلتي:

export interface Dict<T> {
  [index: string]: T;

  [index: number]: T;
}

const keyMap: Dict<number> = {};

function set<T extends object>(index: keyof T) {
  keyMap[index] = 1; // Error Type 'keyof T' cannot be used to index type 'Dict<number>'
}

لكن هذا أيضًا لا يعمل ، لأن الرمز لا يمكن أن يكون نوع فهرس.

export interface Dict<T> {
  [index: string]: T;
  [index: symbol]: T; // Error: An index signature parameter type must be 'string' or 'number'
  [index: number]: T;
}

سلوك متوقع:
يجب أن يكون symbol نوع فهرس صالحًا

السلوك الفعلي:
symbol ليس نوع فهرس صالحًا

يبدو أن استخدام الحل البديل مع إرسال as string | number سيئ جدًا بالنسبة لي.

كيف من المفترض أن يتم استخدام util.promisify.custom في TypeScript؟ يبدو أن استخدام الرموز الثابتة مدعوم الآن ، ولكن فقط إذا تم تحديدها بوضوح. هذا هو TypeScript صالح (بصرف النظر عن f لم يتم تهيئته):
typescript const custom = Symbol() interface PromisifyCustom<T, TResult> extends Function { [custom](param: T): Promise<TResult> } const f: PromisifyCustom<string, void> f[custom] = str => Promise.resolve()
ولكن إذا تم استخدام promisify.custom بدلاً من custom ، فإن محاولة الإشارة إلى f[promisify.custom] تؤدي إلى ظهور الخطأ Element implicitly has an 'any' type because type 'PromisifyCustom<string, void>' has no index signature. :
typescript import {promisify} from 'util' interface PromisifyCustom<T, TResult> extends Function { [promisify.custom](param: T): Promise<TResult> } const f: PromisifyCustom<string, void> f[promisify.custom] = str => Promise.resolve()
أرغب في تخصيص حقل promisify.custom للوظيفة ، لكن يبدو (بالنظر إلى السلوك الموضح أعلاه) أن الطريقة الوحيدة للقيام بذلك هي تحويل الوظيفة إلى نوع any .

لا أستطيع أن أفهم لماذا لا يُسمح بالرمز كمؤشر مفتاح ، يجب أن يعمل الرمز أدناه ويتم قبوله بواسطة Typescript 2.8 ولكن لا يُسمح به بواسطة Typescript 2.9

/**
 * Key can only be number, string or symbol
 * */
export class SimpleMapMap<K extends PropertyKey, V> {
  private o: { [k: string ]: V } = {};

  public has (k: K): boolean {
    return k in this.o;
  }

  public get (k: K): V {
    return this.o[k as PropertyKey];
  }

  public set (k: K, v: V) {
    this.o[k as PropertyKey] = v;
  }

  public getMap (k: K): V {
    if (k in this.o) {
      return this.o[k as PropertyKey];
    }
    const res = new SimpleMapMap<K, V>();
    this.o[k as PropertyKey] = res as any as V;
    return res as any as V;
  }

  public clear () {
    this.o = {};
  }
}

لقد جربت أدناه ، وهو أكثر "صحيح" بالنسبة لي ولكن لم يتم قبوله من قبل كلا الإصدارين من مترجم Typescript

/**
 * Key can only be number, string or symbol
 * */
export class SimpleMapMap<K extends PropertyKey, V> {
  private o: { [k: K ]: V } = {};

  public has (k: K): boolean {
    return k in this.o;
  }

  public get (k: K): V {
    return this.o[k];
  }

  public set (k: K, v: V) {
    this.o[k] = v;
  }

  public getMap (k: K): V {
    if (k in this.o) {
      return this.o[k];
    }
    const res = new SimpleMapMap<K, V>();
    this.o[k as PropertyKey] = res as any as V;
    return res as any as V;
  }

  public clear () {
    this.o = {};
  }
}

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

beenotung على الرغم من أن هذا ليس حلاً مثاليًا ، بافتراض أن الفصل الذي نشرته هو المكان الوحيد الذي تحتاج فيه إلى مثل هذا السلوك ، يمكنك القيام مع الاحتفاظ بتوقيعات الفصل والطرق كما هي ، بحيث يكون المستهلكون من الفصل لن يروا أن:

/**
 * Key can only be number, string or symbol
 * */
export class SimpleMapMap<K extends PropertyKey, V> {
  private o: { [k: string]: V } = {};

  public has(k: K): boolean {
    return k in this.o;
  }

  public get(k: K): V {
    return this.o[k as any];
  }

  public set(k: K, v: V) {
    this.o[k as any] = v;
  }

  public getMap(k: K): V {
    if (k in this.o) {
    return this.o[k as any];
    }

    const res = new SimpleMapMap<K, V>();
    this.o[k as any] = res as any as V;
    return res as any as V;
  }

  public clear() {
    this.o = {};
  }
}

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

مثال على المستهلك يشبه أدناه (لن يحتاج الكود إلى أي تغيير عند إصلاح هذه المشكلة):

const s1 = Symbol(1);
const s2 = Symbol(2);

let m = new SimpleMapMap<symbol, number>()
m.set(s1, 1);
m.set(s2, 2);
m.get(s1);
m.get(1); //error

تم التعرّض للعض من هذا الإصدار 3.0.1.
أريد سجلاً يقبل symbol لكن TS لن يسمح لي بذلك.

لقد مرت 3.5 سنوات منذ فتح هذا الإصدار ، هل يمكننا الحصول على رموز الآن ، من فضلك 🙏

المفارقة هي أن TS تناقض نفسها.
يقوم TS بتوسيع keyof any = number | string | symbol .

ولكن بعد ذلك عندما تفعل record[symbol] ترفض TS القول
لا يمكن استخدام _Type "رمز" كمفهرس_.

نعم ، لقد كنت أعاني من هذا منذ لحظة حزينة ، سؤالي الأخير بخصوص هذا الموضوع:

https://stackoverflow.com/questions/53404675/ts2538-type-unique-symbol-cannot-be-used-as-an-index-type

RyanCavanaughDanielRosenwassermhegazy أي التحديثات؟ هذا العدد يقترب من عيد ميلاده الرابع.

إذا استطاع أحد أن يوجهني في الاتجاه الصحيح ، يمكنني أن أجربه. إذا كانت هناك اختبارات لمطابقتها ، فهذا أفضل.

jhpratt هناك هنا . يبدو أنه يُنظر إليه على أنه قضية هامشية / ذات تأثير منخفض ، لذلك ربما تساعد المزيد من الأصوات المؤيدة على العلاقات العامة في رفع مستوى القضية.

شكرا @ yortus. لقد سألت ريان للتو عما إذا كان PR لا يزال مخططًا لـ 3.2 ، وهو ما يشير إليه المعلم. نأمل أن يكون هذا هو الحال ، وسيتم حل هذا!

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

حل مؤقت هنا https://github.com/Microsoft/TypeScript/issues/24587#issuecomment -412287117 ، قبيح نوعًا ما ولكنه ينجز المهمة

const DEFAULT_LEVEL: string = Symbol("__default__") as any;

آخر https://github.com/Microsoft/TypeScript/issues/24587#issuecomment -460650063 ، منذ linters h8 any

const ItemId: string = Symbol('Item.Id') as unknown as string;
type Item = Record<string, string>;
const shoes: Item = {
  name: 'whatever',
}
shoes[ItemId] = 'randomlygeneratedstring'; // no error
{ name: 'whatever', [Symbol(Item.Id)]: 'randomlygeneratedstring' }

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

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


تحرير: تبين أن هذا الأسلوب يعمل فقط مع Record<X,Y> لكن ليس Interfaces . انتهى الأمر باستخدام // @ts-ignore في الوقت الحالي نظرًا لأنه لا يزال صحيحًا من الناحية النحوية ولا يزال يُترجم جيدًا إلى JS تمامًا كما ينبغي.

من الأشياء التي يجب ملاحظتها عند استخدام // @ts-ignore على الأسطر التي تتضمن رموزًا ، من الممكن بالفعل (ويساعد) تحديد نوع هذا الرمز يدويًا. لا يزال VSCode نوعًا ما يلتقطه.

const id = Symbol('ID');

interface User {
  name: string;
  age: number;
}

const alice: User = {
  name: 'alice',
  age: 25,
};

// @ts-ignore
alice[id] = 'maybeSomeUUIDv4String';

// ...

// then somewhere, when you need this User's id

// @ts-ignore
const id: string = alice[id];

console.log(id); // here you can hover on id and it will say it's a string

لا أعرف ، إذا كان أي شخص قد بدأ شيئًا ما لإصلاح هذا ، ولكن إذا لم يكن كذلك ، فقد فعلت الآن.

وقتي محدود ، على الرغم من عدم علمي بمصادر التنضيد. لقد قمت بعمل مفترق (https://github.com/Neonit/TypeScript) ، ولكن لا يوجد طلب سحب ، حتى الآن ، لأنني لا أريد التحرش بالمطورين من خلال تغييرات (؟) غير مكتملة. أود أن أطلب من الجميع المساهمة بما يمكنهم في مفترقتي. سأصدر في النهاية PR ثم.

لقد وجدت حتى الآن طريقة لإصلاح تقييد نوع فهرس الواجهة. لا أعلم ، إذا كان هناك المزيد. تمكنت من فهرسة كائن برمز في TS 3.4 دون أي إصلاحات. ( https://www.typescriptlang.org/play/#src = const٪ 20o٪ 20٪ 3D٪ 20٪ 7B٪ 7D٪ 3B٪ 0D٪ 0Aconst٪ 20s٪ 20٪ 3D٪ 20Symbol ('s')٪ 3B ٪ 0D٪ 0A٪ 0D٪ 0Ao٪ 5Bs٪ 5D٪ 20٪ 3D٪ 20123٪ 3B)

ألق نظرة على التزامي لترى ما وجدته: https://github.com/Neonit/TypeScript-SymbolKeys/commit/11cb7c13c2494ff32cdec2d4f82673058c825dc3

مفقود:

  • الاختبارات: لم يتح لي الوقت للنظر في كيفية تنظيم وتنظيم الاختبارات في TS.
  • الترجمة: تم تحديث رسالة التشخيص فقط لمتغير اللغة الإنجليزية. ربما اللغات الأخرى لا تزال تتلقى الرسالة القديمة. لا أدري، لا أعرف. يمكنني فقط تقديم ترجمة ألمانية على أي حال.

آمل أن تبدأ الأمور أخيرًا بعد سنوات من الانتظار.

يبدو الإصلاح جيدًا. هل يستطيع مطور TypeScript إلقاء نظرة عليه؟

مرحبا ، أي تقدم في هذا؟

فتحت للتو موضوع SO حول هذا: https://stackoverflow.com/questions/59118271/using-symbol-as-object-key-type-in-typescript

لماذا هذا غير ممكن؟ أليس symbol نوعًا بدائيًا آخر مثل number - فلماذا هناك فرق؟

مرحبا ، أي تقدم لهذا؟

لقد مرت خمس سنوات!

لن تصدق المدة التي استغرقتها C ++ للحصول على الإغلاق 😲

lol fair ، لكن C ++ لا تسوّق نفسها كمجموعة شاملة للغة بها إغلاق :-p

ljharb استمر بضرب هذا الحصان ، ما زال يرتعش 😛

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

let m = new Map<symbol, number>();
let s = Symbol("arbitrary symbol!");

m.set(s, 1000);
let a = m.get(s);


الخرائط والأشياء لها حالات استخدام مختلفة.

DanielRosenwasser الرموز المعروفة تستخدم كبروتوكولات ؛ مفتاح الخريطة Symbol.match ، على سبيل المثال ، لن يجعل الكائن يشبه RegExp (وقد يحتاج أي كائن إلى مفتاح Symbol.iterable لجعله قابلاً للتكرار دون الحاجة إلى استخدام TS بشكل صريح أنواع قابلة للتكرار).

ما يقرب من 5 سنوات (

من فضلك ، نفذ هذه الميزة ، لا يمكنني كتابة الكود بشكل طبيعي ..

هل يمكن للمشاركين تقديم أمثلة فعلية في حالات الاستخدام الخاصة بهم؟

لا أفهم مثال البروتوكول ولماذا ليس ممكنًا اليوم.

هذا مثال على StringConvertible

const intoString = Symbol("intoString")

/**
 * Something that can be converted into a string.
 */
interface StringConvertible {
    [intoString](): string;
}

/**
 * Something that is adorable.
 */
class Dog implements StringConvertible {
    [intoString](): string {
        return "RUFF RUFF";
    }
}

/**
 * <strong i="9">@see</strong> {https://twitter.com/drosenwasser/status/1102337805336768513}
 */
class FontDog implements StringConvertible {
    [intoString](): string {
        return "WOFF WOFF";
    }
}

console.log(new Dog()[intoString]())
console.log(new FontDog()[intoString]())

فيما يلي مثال على Mappable أو Functor (عدم وجود مُنشئات من النوع ذي الترتيب الأعلى جانبًا):

const map = Symbol("map")

interface Mappable<T> {
    [map]<U>(f: (x: T) => U): Mappable<U>
}

class MyCoolArray<T> extends Array<T> implements Mappable<T> {
    [map]<U>(f: (x: T) => U) {
        return this.map(f) as MyCoolArray<U>;
    }
}

DanielRosenwasser يبدو أنك تفترض أن جميع الكائنات لها واجهة أو هي نسخة من فئة أو معروفة مسبقًا ؛ باستخدام المثال الأخير ، يجب أن أكون قادرًا على تثبيت map ، على سبيل المثال ، على أي كائن جافا سكريبت (أو على الأقل ، كائن يسمح نوعه بإضافة أي رمز إليه) ، مما يجعله قابلاً للتخطيط.

يعد تثبيت خاصية (رمز أم لا) على كائن بعد الحقيقة جزءًا من طلب ميزة مختلف (يُطلق عليه غالبًا "خصائص expando" أو "نوع expando").

في حالة عدم وجود ذلك ، فإن النوع الذي تحتاجه لتوقيع فهرس الرموز سيوفر القليل جدًا كمستخدم TypeScript ، أليس كذلك؟ إذا فهمت بشكل صحيح ، يجب أن يكون النوع مثل unknown أو any ليكون مفيدًا إلى حد ما.

interface SymbolIndexable {
   [prop: symbol]: any; // ?
}

في حالة البروتوكولات ، هي بشكل عام وظيفة ، ولكن بالتأكيد ، يمكن أن تكون unknown .

ما أحتاجه هو رمز (و bigint) يعادل type O = { [k: string]: unknown } ، لذا يمكنني تمثيل كائن JS فعلي (شيء يمكن أن يحتوي على أي نوع من المفاتيح) مع نظام الكتابة. يمكنني تضييق ذلك لاحقًا حسب الحاجة ، لكن النوع الأساسي لكائن JS سيكون { [k: string | bigint | symbol | number]: unknown } ، بشكل أساسي.

آه أعتقد أنني أرى نقطة DanielRosenwasser . لدي حاليًا رمز بواجهة مثل:

export interface Environment<T> {
    [Default](tag: string): Intrinsic<T>;
    [Text]?(text: string): string;
    [tag: string]: Intrinsic<T>;
    // TODO: allow symbol index parameters when typescript gets its shit together
    // [tag: symbol]: Intrinsic<T>;
}

حيث Intrinsic<T> هو نوع دالة ، وأريد السماح للمطورين بتعريف خصائص الرمز الخاصة بهم في بيئات مشابهة للسلاسل ، ولكن بقدر ما يمكنك إضافة [Symbol.iterator] ، [Symbol.species] أو خصائص الرمز المخصصة لأي واجهة ، فإن توقيع الفهرس بالرموز سيقيد بشكل غير صحيح أي كائنات تنفذ هذه الخصائص.

إذن ما تقوله هو أنه لا يمكنك جعل نوع قيمة الفهرسة بالرمز أكثر تحديدًا من any ؟ هل يمكننا بطريقة ما استخدام التمييز unique symbol مقابل symbol للسماح بذلك؟ مثل ، هل يمكننا جعل توقيع الفهرس افتراضيًا للرموز العادية والسماح للرموز الفريدة / المعروفة بتجاوز نوع الفهرس؟ حتى لو لم تكن آمنة للطباعة ، فإن القدرة على الحصول على / تعيين الخصائص من خلال فهارس الرموز بشكل تعسفي ستكون مفيدة.

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

DanielRosenwasser هنا مثال حقيقي لكود الإنتاج الخاص بي. دولة يُعاد استخدامها في العديد من الأماكن كخريطة وقد تقبل مفتاح الذرة (ميزة مسيطر عليها). أحتاج حاليًا إلى إضافة دعم الرموز ، لكنني أحصل على الكثير من الأخطاء:


على أي حال ، السلوك الحالي غير متوافق مع معيار ES وهو خطأ.

كان لدي فكرة أخرى في وقت متأخر من الليل بشأن أنواع الرموز. لماذا هذا ليس خطأ؟

const foo = {
  [Symbol.iterator]: 1,
}

تتوقع JS أن تكون جميع خصائص Symbol.iterator دالة تقوم بإرجاع مكرر ، وهذا الكائن قد يكسر الكثير من التعليمات البرمجية إذا تم تمريره في أماكن مختلفة. إذا كانت هناك طريقة لتعريف خصائص الرمز بشكل عام لجميع الكائنات ، فيمكننا السماح بتوقيعات فهرس رمز معينة مع السماح أيضًا بالتجاوزات العامة. ستكون آمنة ، أليس كذلك؟

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

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

لم أقم فقط بطلب سحب ، لأنني لا أعرف عن إطار عمل أو متطلبات الاختبار الخاصة بـ Typescript ولأنني لا أعرف ما إذا كانت التغييرات في الملفات المختلفة ضرورية لإنجاز هذا العمل في جميع الحالات.

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

بصرف النظر عن كل ما يتعلق بحالة الاستخدام العام ، إذا كنت تريد تعيين القيم على رموز عشوائية. أو للتوافق مع كود ES6 غير المكتوب.

إليك مثال على مكان أعتقد أنه سيكون مفيدًا: https://github.com/choojs/nanobus/pull/40/files. من الناحية العملية ، يمكن أن يكون eventName s رموزًا أو سلاسل ، لذا أود أن أكون قادرًا على قول

type EventsConfiguration = { [eventName: string | Symbol]: (...args: any[]) => void }

في السطر الأول.

لكن ربما أكون قد أسيء فهم شيء ما حول كيفية القيام بذلك.

لا يمكن استخدام حالة الاستخدام البسيطة بدون ألم:

type Dict<T> = {
    [key in PropertyKey]: T;
};

function dict<T>() {
    return Object.create(null) as Dict<T>;
}

const has: <T>(dict: Dict<T>, key: PropertyKey) => boolean = Function.prototype.call.bind(Object.prototype.hasOwnProperty);

function forEach<T>(dict: Dict<T>, callbackfn: (value: T, key: string | symbol, dict: Dict<T>) => void, thisArg?: any) {
    for (const key in dict)
        if (has(dict, key))
            callbackfn.call(thisArg, dict[key], key, dict);
    const symbols = Object.getOwnPropertySymbols(dict);
    for (let i = 0; i < symbols.length; i++) {
        const sym = symbols[i];
        callbackfn.call(thisArg, dict[sym], sym, dict); // err
    }
}

const d = dict<boolean>();
const sym = Symbol('sym');
const bi = 9007199254740991n;

d[1] = true;
d['x'] = true;
d[sym] = false; // definitely PITA
d[bi] = false; // another PITA

forEach(d, (value, key) => console.log(key, value));

أنا أيضًا لا أفهم سبب الحاجة إلى حالة استخدام هنا.

neonit ، هناك ذلك -

يبدو أنه في الواقع ، لن يتم حل حالات الاستخدام المتخيلة لمعظم الأشخاص بالسهولة التي يتصورونها (راجع استجابة الخرائط (https://github.com/microsoft/TypeScript/issues/1863 # issuecomment -572733050).

أعتقد أن @ Tyler-Murphy قد أعطى أفضل مثال هنا في أنه لا يمكنك كتابة قيود ، والتي يمكن أن تكون مفيدة جدًا لشيء مثل باعث الأحداث الآمن من النوع الذي يدعم الرموز.

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

من السهل دائمًا قول ذلك عندما لا تضطر إلى الحفاظ على المشروع! 😄 أفهم أن هذا شيء مفيد لك ولكن أتمنى أن تحترمه.

هذا هو عدم توافق ES6

هناك الكثير من التركيبات التي لا يستطيع TypeScript كتابتها بسهولة لأنه سيكون غير ممكن. أنا لا أقول أن هذا مستحيل ، لكنني لا أعتقد أن هذه طريقة مناسبة لتأطير هذه القضية.

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

const Answerable = Symbol.for("Answerable");
declare global {
  interface KnownSymbols {
    [Answerable](): string  | number;
  }
}

interface MyObject {
  [name: symbol]: boolean;
}

const MySymbol = Symbol.for("MySymbol");
const obj: MyObject = {
  [MySymbol]: true,
};

obj[Answerable] = () => "42";

من خلال الإعلان عن خصائص إضافية على واجهة KnownSymbols ، فإنك تسمح بفهرسة جميع الكائنات بواسطة هذا الرمز وتقييد قيمة الخاصية إلى نوع القيمة undefined / your value. هذا من شأنه أن يوفر قيمة على الفور من خلال السماح للطباعة على الحروف بتوفير كتابة للرموز المعروفة التي يوفرها ES6. من الواضح أن إضافة خاصية Symbol.iterator إلى كائن ليس دالة تقوم بإرجاع مكرر يجب أن تكون خطأ ، لكنها ليست واحدة في الكتابة المطبوعة حاليًا. وسيجعل إضافة خصائص الرموز المعروفة إلى الكائنات الموجودة بالفعل أسهل بكثير.

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

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

حالات الاستخدام الفردية ليست ذات صلة. إذا كانت JavaScript قاسية ، فيجب أن يتم التعبير عنها في تعريفات TS.

لكن ما أفهمه هو أن هناك مشكلات دقيقة تتعلق بكيفية تفاعل الميزة مع بقية نظام الكتابة

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

أدى في الغالب إلى مناقشة حول كيفية فشلنا في التعرف على الأنواع المغلقة مقابل الأنواع المفتوحة. في هذا السياق ، سيكون النوع "مغلق" نوعًا له مجموعة محدودة من المفاتيح التي لا يمكن تمديد قيمها. مفاتيح من نوع دقيق ، إذا صح التعبير. في الوقت نفسه ، النوع "المفتوح" في هذا السياق هو نوع يكون مفتوحًا لإضافة المزيد من المفاتيح عند التصنيف الفرعي (والتي ، بموجب قواعد التصنيف الفرعي الحالية لدينا ، تكون جميع الأنواع في الغالب أحيانًا ، باستثناء الأنواع التي لها توقيعات فهرسة صريحة جدًا) . تشير توقيعات الفهرس إلى إنشاء نوع مفتوح ، بينما ترتبط الأنواع المعينة إلى حد كبير كما لو كانت تعمل على أنواع مغلقة. يعمل هذا _ عادةً_ بشكل جيد بما فيه الكفاية لأن معظم التعليمات البرمجية ، عمليًا ، تتم كتابتها بهيكل متوافق مع أنواع الكائنات المغلقة. هذا هو السبب في أن flow (الذي يحتوي على بناء جملة صريح لأنواع الكائنات المغلقة مقابل أنواع الكائنات المفتوحة) افتراضيًا لأنواع الكائنات المغلقة. يأتي هذا إلى رأس مع مفاتيح الفهرس العامة ؛ إذا كان لدي T extends string ، لأن T يتم إنشاء مثيل له من أنواع أوسع وأوسع (من "a" إلى "a" | "b" إلى string ) ، الكائن المنتج أكثر تخصصًا ، حتى نتبادل من "a" | "b" | ... (every other possible string) إلى string نفسه. بمجرد حدوث ذلك ، يصبح النوع مفتوحًا جدًا فجأة ، وبينما من المحتمل أن تكون كل خاصية موجودة للوصول إليها ، يصبح قانونيًا ، على سبيل المثال ، تعيين كائن فارغ لها. هذا ما يحدث من الناحية الهيكلية ، ولكن عندما نربط بين الأدوية الجنيسة في الأنواع المعينة ، فإننا نتجاهل ذلك - القيد string على مفتاح نوع معين مرتبط بشكل أساسي كما لو أنه يجعل كل المفاتيح الممكنة موجودة. يتبع هذا منطقيًا عرضًا بسيطًا قائمًا على التباين لنوع المفتاح ، ولكن الصحيح فقط هو أن المفاتيح تأتي من نوع _closed_ (والذي ، ofc ، نوع به توقيع فهرس لا يتم إغلاقه فعليًا!). لذلك ، إذا أردنا أن نكون متوافقين مع الإصدارات السابقة ، فلا يمكننا أن نتعامل مع {[x: T]: U} مثل {[_ in T]: U} ، ما لم نرغب في ذلك ، لأنه في الحالة غير العامة {[_ in T]: U} يصبح {[x: T]: U} ، اضبط الطريقة التي نتعامل بها مع تباين مفاتيح النوع المعين لحساب النوع المفتوح "edge" بشكل صحيح ، وهو تغيير مثير للاهتمام في حد ذاته يمكن أن يكون له تداعيات على النظام البيئي.

إلى حد كبير: نظرًا لأنها تقرب الأنواع المعينة وتوقيعات الفهرس من بعضها ، فقد أثارت مجموعة من الأسئلة حول كيفية تعاملنا مع كليهما والتي ليس لدينا إجابات مرضية أو قاطعة عليها حتى الآن.

حالات الاستخدام الفردية ليست ذات صلة.

هذا جنون خالص بأدب. كيف يمكننا أن نعرف ما إذا كنا نضيف ميزة بالسلوك الذي يريده الأشخاص بدون حالات استخدام يمكن من خلالها الحكم على هذا السلوك أم لا؟

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

أنت تطلب منا أن نطير بالعمى. من فضلك لا! من فضلك أخبرنا أين تريد أن تذهب الطائرة!

سيئتي ، كان بإمكاني أن أكون أكثر وضوحًا بشأن ما قصدته ؛ بدا لي أن الناس شعروا بضرورة تبرير حالات استخدام الشفرة الفعلية الخاصة بهم ، بدلاً من رغبتهم في وصفها بدقة أكبر عبر TS

لذلك إذا فهمتها بشكل صحيح ، فهذا يتعلق بشكل أساسي بالمشكلة التالية:

const sym = Symbol();
interface Foo
{
    [sym]: number;
    [s: symbol]: string; // just imagine this would be allowed
}

الآن سوف يرى مترجم Typescript هذا على أنه تعارض ، لأن Foo[sym] له نوع متناقض. لدينا نفس المشكلة مع السلاسل بالفعل.

interface Foo
{
    ['str']: number; // <-- compiler error: not assignable to string index type 'string'
    [s: string]: string;
}

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

أعتقد أنه بالنسبة للرموز ستكون هذه مشكلة منتشرة في كل مكان ، لأن ECMA2015 تحدد الرموز القياسية مثل Symbol.iterator ، والتي يمكن استخدامها على أي كائن وبالتالي يجب أن يكون لها كتابة افتراضية. وهو أمر غريب ليس لديهم على ما يبدو. على الأقل لا يسمح لي الملعب بتشغيل مثال Symbol.iterator من MDN .

بافتراض أنه من المخطط إضافة أنواع رموز محددة مسبقًا ، فسيؤدي ذلك دائمًا إلى تعريف [s: symbol]: SomeType ليكون غير صالح ، لأن فهارس الرموز المحددة مسبقًا بها أنواع غير متوافقة بالفعل ، لذا لا يمكن أن يوجد نوع عام شائع أو ربما تحتاج إلى أن تكون من النوع function ، لأن معظم (/ الكل؟) مفاتيح الرموز المحددة مسبقًا هي من النوع function .

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

const foo: Foo = {str: 42, a: 'one', b: 'two'};
const input: string = getUserInput();
const value = foo[input];

ستنطبق نفس المشكلة على مفاتيح الرموز. من المستحيل تحديد نوع value بالضبط في وقت الترجمة. إذا أدخل المستخدم 'str' ، فسيكون number ، وإلا فسيكون string (على الأقل يتوقع Typescript أن يكون string ، بينما هو من المحتمل أن يصبح undefined ). هل هذا هو سبب عدم وجود هذه الميزة؟ يمكن للمرء حل هذا عن طريق إعطاء value نوع اتحاد يحتوي على جميع الأنواع الممكنة من التعريف (في هذه الحالة number | string ).

Neonit حسنًا ، هذه ليست المشكلة التي أوقفت التقدم في التنفيذ ، ولكن هذه بالضبط واحدة من المشكلات التي أحاول أن أشير إليها - اعتمادًا على ما تحاول القيام به ، قد لا تكون مفهرسات الرموز هي الحل.

إذا تم تنفيذ هذه الميزة ، فلن تؤدي الرموز المضمنة في ECMAScript بالضرورة إلى تدمير كل شيء لأنه لا يستخدم كل نوع هذه الرموز ؛ ولكن أي نوع التي لا تحدد خاصية مع رمز معروف (أو أي رمز أنك نفسك تعريف) من المرجح أن يقتصر على توقيع المؤشر أقل من المفيد للحرف.

هذا هو الشيء الذي يجب أخذه في الاعتبار - إن حالات الاستخدام "أريد استخدام هذا كخريطة" و "أريد استخدام الرموز لتنفيذ البروتوكولات" غير متوافقة من منظور نظام النوع. لذلك إذا كان لديك أي شيء من هذا القبيل في الاعتبار ، فقد لا تساعدك توقيعات فهرس الرموز ، وقد يتم تقديمك بشكل أفضل عبر خصائص أو خرائط رمز صريحة.

ماذا عن شيء مثل نوع UserSymbol هذا فقط symbol ناقص الرموز المضمنة؟ تضمن طبيعة الرموز عدم حدوث تصادمات عرضية على الإطلاق.

تحرير: التفكير في هذا الأمر أكثر ، الرموز المعروفة هي مجرد حراس يتم تنفيذها باستخدام Symbol . ما لم يكن الهدف هو تسلسل الكائن أو الاستبطان ، فمن المحتمل أن تعامل الكود هؤلاء الحراس بشكل مختلف عن الرموز الأخرى ، لأن لديهم معنى خاصًا للغة. من المحتمل أن تؤدي إزالتها من النوع symbol إلى جعل (معظم) الكود باستخدام الرموز "العامة" أكثر أمانًا.

RyanCavanaugh هذه خطة رحلتي الجوية.

لدي نظام أستخدم فيه رموزًا مثل هذه للخصائص.

const X = Symbol.for(":ns/name")

const txMap = {
  [X]: "fly away with me!"
}

transact(txMap) // what's the index signature here?

في هذه الحالة ، أريد أن يتلاءم txMap مع نوع التوقيع transact . لكن على حد علمي لا يمكنني التعبير عن هذا اليوم. في حالتي ، transact هو جزء من مكتبة لا تعرف ما يمكن توقعه. أفعل شيئًا كهذا للممتلكات.

// please forgive my tardiness but in essence this is how I'm typing "TxMap" for objects
type TxMapNs = { [ns: string]: TxMapLocal }
type TxMapLocal = { [name: string]: string | TxMapNs } // leaf or non leaf

يمكنني إنشاء مجموعة الأنواع التي تناسب transact من المخطط واستخدام ذلك. لذلك سأفعل شيئًا كهذا واعتمد على دمج التصريحات.

interface TxMap = {
  [DB_IDENT]: symbol // leaf
  [DB_VALUE_TYPE]?: TxMap // not leaf
  [DB_CARDINALITY]?: TxMap
}

ولكن سيكون من الجيد لو تمكنت على الأقل من الرجوع إلى توقيع فهرس للرموز ، أتوقع فقط transact أن يتم تسليم كائنات JavaScript عادية ، كما أنني أستخدم الرموز من سجل الرموز العام فقط في هذه الحالة. أنا لا أستخدم الرموز الخاصة.


يجب أن أضيف أن هذا مؤلم قليلاً.

const x = Symbol.for(":x");
const y = Symbol.for(":x");

type X = { [x]: string };
type Y = { [y]: string };

const a: X = { [x]: "foo" };
const b: Y = { [x]: "foo" }; // not legal
const c: X = { [y]: "foo" }; // not legal
const d: Y = { [y]: "foo" };

سيكون من الرائع جدًا أن يفهم TypeScript أن الرموز التي تم إنشاؤها عبر الوظيفة Symbol.for هي نفسها بالفعل.


هذا أيضًا مزعج للغاية.

function keyword(ns: string, name: string): unique symbol { // not possible, why?
  return Symbol.for(":" + ns + "/" + name)
}

const x: unique symbol = keyword("db", "id") // not possible, why?

type X = {
  [x]: string // not possible, why?
}

هذه الوظيفة المفيدة الصغيرة تسمح لي بفرض اتفاقية على جدول الرموز العالمي الخاص بي. ومع ذلك ، لا يمكنني إرجاع unique symbol ، حتى لو تم إنشاؤه عبر الوظيفة Symbol.for . بسبب الطريقة التي يعمل بها TypeScript ، فإنه يجبرني على التخلي عن حلول معينة. هم فقط لا يعملون. وأعتقد أن هذا محزن.

لقد صادفت حالة استخدام أخرى حيث يكون symbol كقيمة فهرسة مفيدًا ، عند العمل مع ES Proxies لإنشاء وظيفة مصنع تقوم بتغليف كائن باستخدام وكيل.

خذ هذا المثال:

let original = {
    foo: 'a',
    bar: 'b',
    baz: 1
};

function makeProxy<T extends Object>(source: T) {
    return new Proxy(source, {
        get: function (target, prop, receiver) {
            return target[prop];
        }
    });
}

let proxied = makeProxy(original);

لمطابقة توقيع النوع ProxyConstructor ، يجب أن يمتد الوسيط العام إلى Object ، ولكن هذا بعد ذلك أخطاء لأن الوسيطة العامة ليست مفتاحًا. حتى نتمكن من تمديد توقيع النوع:

function makeProxy<T extends Object & { [key: string]: any}>(source: T) {

ولكن الآن ستظهر خطأ لأن الوسيطة الثانية ( prop ) لـ get على ProxyHandler من النوع PropertyKey والتي تصادف أنها PropertyKey .

لذلك لست متأكدًا من كيفية القيام بذلك باستخدام TypeScript نظرًا لقيود هذه المشكلة.

aaronpowell ما هي المشكلة التي تواجهها؟ أرى أنه يتصرف بشكل جيد:

let original = {
    foo: 'a',
    bar: 'b',
    baz: 1
};

function makeProxy<T extends Object>(source: T) {
    return new Proxy(source, {
        get: function (target, prop, receiver) {
            return target[prop];
        }
    });
}

let proxied = makeProxy(original);

function assertString(s:string){}
function assertNumber(x:number){}

assertString(proxied.foo); // no problem as string
assertNumber(proxied.baz); // no problem as number
console.log(proxied.foobar); // fails as expected: error TS2339: Property 'foobar' does not exist on type '{ foo: string; bar: string; baz: number; }'.

tsconfig.json:

{
  "compilerOptions": {
    "module": "esnext",
    "moduleResolution": "node",
    "target": "es2015"
  }

package.json:

{
  "devDependencies": {
    "typescript": "~3.4.5"
  }
}

beenotung أرى خطأ في الملعب:

image

aaronpowell يظهر الخطأ عند تمكين علامة "صارمة" في " tsconfig.json .

لذلك ، في ظل الإصدار الحالي من برنامج التحويل البرمجي من نوع الكتابة ، يتعين عليك إما إيقاف تشغيل الوضع المقيد أو تحويل الهدف إلى any ...

بالتأكيد ، ولكن لا يعد خيار المصبوب any مثاليًا حقًا ، كما أن تعطيل الوضع الصارم يؤدي فقط إلى تخفيف القيود في أمان النوع.

قراءة الرسائل أتصور أن "الحل" التالي سيكون على الأرجح "تعطيل الكتابة المطبوعة".

لا ينبغي علينا البحث عن حلول مؤقتة ولا يجب علينا شرح سبب حاجتنا إليها.

إنها ميزة قياسية لجافا سكريبت لذا فنحن بحاجة إليها في الكتابة المطبوعة.

DanielRosenwasser حالة لحالة aaronpowell @ - يبدو أن عدم التطابق في واجهة ProxyHandler وقواعد TypeScript تمنعني من كتابة اعتراضات معالج الوكيل بشكل صحيح.

مثال موجز يوضح المشكلة:

const getValue = (target: object, prop: PropertyKey) => target[prop]; // Error

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

أنا مبتدئ في TypeScript ، لذا يرجى أن تسامحني إذا فقدت شيئًا واضحًا.

حالة استخدام أخرى: أحاول الحصول على نوع {[tag: symbol]: SomeSpecificType} للمتصلين لتقديم خريطة للقيم ذات العلامات من نوع معين بطريقة تستفيد من تماسك بناء الجملة الحرفية للكائن (مع تجنب تعارض الاسم مخاطر استخدام السلاسل العادية كعلامات).

حالة استخدام أخرى: أحاول تكرار كل الخصائص المعدودة لكائن ورموز وسلاسل. يبدو الرمز الحالي الخاص بي على هذا النحو (الأسماء محجوبة):

type ContextKeyMap = Record<PropertyKey, ContextKeyValue>

function setFromObject(context: Context, object: ContextKeyMap) {
    for (const key in object) {
        if (hasOwn.call(object, key)) context.setKey(key, object[key])
    }

    for (const symbol of Object.getOwnPropertySymbols(object)) {
        if (propertyIsEnumerable.call(object, symbol)) {
            context.setKey(symbol, object[symbol as unknown as string])
        }
    }
}

أنا أفضل بشدة أن أكون قادرًا على القيام بذلك:

type ContextKeyMap = Record<PropertyKey, ContextKeyValue>

function setFromObject(context: Context, object: ContextKeyMap) {
    for (const key in object) {
        if (hasOwn.call(object, key)) context.setKey(key, object[key])
    }

    for (const symbol of Object.getOwnPropertySymbols(object)) {
        if (propertyIsEnumerable.call(object, symbol)) {
            context.setKey(symbol, object[symbol])
        }
    }
}

لدي أيضًا مشكلة في فهرسة الرموز. الكود الخاص بي على النحو التالي:

const cacheProp = Symbol.for('[memoize]')

function ensureCache<T extends any>(target: T, reset = false): { [key in keyof T]?: Map<any, any> } {
  if (reset || !target[cacheProp]) {
    Object.defineProperty(target, cacheProp, {
      value: Object.create(null),
      configurable: true,
    })
  }
  return target[cacheProp]
}

لقد اتبعت الحل من خلال aaronpowell وتمكنت بطريقة ما من

const cacheProp = Symbol.for('[memoize]') as any

function ensureCache<T extends Object & { [key: string]: any}>(target: T, reset = false): { [key in keyof T]?: Map<any, any> } {
  if (reset || !target[cacheProp]) {
    Object.defineProperty(target, cacheProp, {
      value: Object.create(null),
      configurable: true,
    })
  }

  return target[cacheProp]
}

الإرسال إلى any من symbol ليس جيدًا حقًا.

أقدر حقًا أي حلول أخرى.

ahnpnl لحالة الاستخدام هذه ، سيكون من الأفضل لك استخدام WeakMap من الرموز ، وستعمل المحركات على تحسين ذلك بشكل أفضل - لا يعدل خريطة النوع target . قد لا يزال يتعين عليك إرساله ، ولكن ستعيش فريقك في القيمة المرتجعة.

الحل هو استخدام دالة عامة لتعيين قيمة ...

var theAnswer: symbol = Symbol('secret');
var obj = {} as Record<symbol, number>;
obj[theAnswer] = 42; // Currently error, but should be allowed

Object.assign(obj, {theAnswer: 42}) // allowed

الحل هو استخدام دالة عامة لتعيين قيمة ...

var theAnswer: symbol = Symbol('secret');
var obj = {} as Record<symbol, number>;
obj[theAnswer] = 42; // Currently error, but should be allowed

Object.assign(obj, {theAnswer: 42}) // allowed

أنا لا أوافق. هذه الأسطر الثلاثة متساوية مع بعضها البعض:

Object.assign(obj, {theAnswer: 42});
Object.assign(obj, {'theAnswer': 42});
obj['theAnswer'] = 42;

تضمين التغريدة
لدي حالة الاستخدام هذه ، في رابط الملعب ، قمت أيضًا بحلها باستخدام الخرائط ، لكن ألق نظرة ، إنها قبيحة.

const system = Symbol('system');
const SomeSytePlugin = Symbol('SomeSytePlugin')

/** I would prefer to have this working in TS */
interface Plugs {
    [key: symbol]: (...args: any) => unknown;
}
const plugins = {
    "user": {} as Plugs,
    [system]: {} as Plugs
}
plugins[system][SomeSytePlugin] = () => console.log('awsome')
plugins[system][SomeSytePlugin](); ....

رابط الملعب

يؤدي استخدام الرموز هنا إلى استبعاد الكتابة العرضية المحتملة التي تحدث عند استخدام السلاسل. يجعل النظام بأكمله أكثر قوة ويسهل صيانته.

إذا كان لديك حل بديل يعمل مع TS ولديه نفس قابلية القراءة في الكود ، فأنا آذان صاغية.

أي شرح رسمي لهذه المسألة؟

الحل هو استخدام دالة عامة لتعيين قيمة ...

var theAnswer: symbol = Symbol('secret');
var obj = {} as Record<symbol, number>;
obj[theAnswer] = 42; // Currently error, but should be allowed

Object.assign(obj, {theAnswer: 42}) // allowed

أنت تبحث عن

Objet.assign(obj, { [theAnswer]: 42 });

ومع ذلك ، لا توجد طريقة لقراءة x[theAnswer] للتراجع بدون فريق AFAIK انظر التعليق الثاني أدناه

من أجل محبة الله ، يرجى جعل هذا أولوية.

أنت تبحث عن

Objet.assign(obj, { [theAnswer]: 42 });

ومع ذلك ، لا توجد طريقة لقراءة x[theAnswer] للتراجع بدون فريق AFAIK

كما أشار mellonis و MingweiSamuel ، فإن الحلول التي تستخدم الوظيفة العامة هي:

var theAnswer: symbol = Symbol("secret");
var obj = {} as Record<symbol, number>;

obj[theAnswer] = 42; // Not allowed, but should be allowed

Object.assign(obj, { [theAnswer]: 42 }); // allowed

function get<T, K extends keyof T>(object: T, key: K): T[K] {
  return object[key];
}

var value = obj[theAnswer]; // Not allowed, but should be allowed

var value = get(obj, theAnswer); // allowed

خمس سنوات والرمز كمؤشر لا يزال غير مسموح به

تم العثور على حل بديل لهذه الحالة ، فهو ليس عامًا ولكنه يعمل في بعض الحالات:

const SYMKEY = Symbol.for('my-key');

interface MyObject {   // Original object interface
  key: string
}

interface MyObjectExtended extends MyObject {
  [SYMKEY]?: string
}

const myObj: MyObject = {
  'key': 'value'
}

// myObj[SYMKEY] = '???' // Not allowed

function getValue(obj: MyObjectExtended, key: keyof MyObjectExtended): any {
  return obj[key];
}

function setValue(obj: MyObjectExtended, key: keyof MyObjectExtended, value: any): void {
  obj[key] = value
}

setValue(myObj, SYMKEY, 'Hello world');
console.log(getValue(myObj, SYMKEY));

@ james4388 كيف

لمعلوماتك: https://github.com/microsoft/TypeScript/pull/26797

(لقد عثرت عليه للتو - لست في الواقع جزءًا من فريق TS.)

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

القضايا ذات الصلة

manekinekko picture manekinekko  ·  3تعليقات

DanielRosenwasser picture DanielRosenwasser  ·  3تعليقات

dlaberge picture dlaberge  ·  3تعليقات

wmaurer picture wmaurer  ·  3تعليقات

MartynasZilinskas picture MartynasZilinskas  ·  3تعليقات