Typescript: اقتراح: النطاق كنوع الرقم

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

عند تحديد نوع يمكن تحديد عدة أرقام مفصولة بـ | .

type TTerminalColors = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15;

السماح بتحديد أنواع الأرقام كنطاقات ، بدلاً من سرد كل رقم:

type TTerminalColors = 0..15;
type TRgbColorComponent = 0..255;
type TUInt = 0..4294967295;

ربما استخدم .. للأعداد الصحيحة و ... للعوامات.

interface Math {
  random(): 0...1
}
In Discussion Suggestion

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

يمكن توسيع هذه الفكرة لتشمل الأحرف ، على سبيل المثال "b".."d" سيكون "b" | "c" | "d" . سيكون من الأسهل تحديد مجموعات الأحرف.

ال 106 كومينتر

يمكن توسيع هذه الفكرة لتشمل الأحرف ، على سبيل المثال "b".."d" سيكون "b" | "c" | "d" . سيكون من الأسهل تحديد مجموعات الأحرف.

أعتقد أنه يمكن توسيع هذا واستخدام مثل النحوية والدلالات مثل نطاقات هاسكل.

| بناء الجملة | ديسوجارد |
| -------------------------- | ---------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ------------------------------------------ |
| type U = (e1..e3) | type U = \| e1 \| e1+1 \| e1+2 \| ...e3 \|
الاتحاد هو never إذا e1 > e3 |
| type U2 = (e1, e2..e3) | type U2 = \| e1 \| e1+i \| e1+2i \| ...e3 \| ،
حيث تكون الزيادة i هي e2-e1 .

إذا كانت الزيادة موجبة أو صفرية ، فسينتهي الاتحاد عندما يكون العنصر التالي أكبر من e3 ؛
الاتحاد هو never إذا e1 > e3 .

إذا كانت الزيادة سالبة ، فسينتهي الاتحاد عندما يكون العنصر التالي أقل من e3 ؛
الاتحاد هو never إذا e1 < e3 . |

panuhorsmalahti ماذا لو حددت "bb".."dd" ؟

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

ربما استخدم .. للأعداد الصحيحة و ... للعوامات.

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

aluanhaddad قل الاحتمال:

type TProbability = 0.0...1.0;

streamich بحيث أن هذا النوع له نظريًا عدد لا حصر له من السكان المحتملين؟

aluanhaddad في الواقع سيكون أبعد ما يكون عن اللانهاية في النقطة العائمة IEEE. سيكون لديها 1،065،353،217 نسمة حسب حساباتي.

0.0...1.0 ؟ يستخدم JS IEEE double ، أي 53 بت من النطاق الديناميكي. إذا تم دعم ذلك ، فيجب أن تكون النطاقات من النوع الأول ، وإلغاء ذلك إلى الاتحاد سيكون غير عملي.

jcready بالفعل ولكن ، كما يشير fatcerberus ، فإن إدراكها كنوع اتحاد سيكون أمرًا باهظًا.

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

إن إدراكه كنوع نقابي سيكون أمرًا باهظًا.

aluanhaddad نعم ، ولكن حتى تحديد عدد صحيح بدون إشارة كاتحاد سيكون مكلفًا للغاية:

type TUInt = 0..4294967295;

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

type UInt = 0..4294967295;
var x: UInt = ......;
if (x !== 4) {
  x;
}

سيكون إنشاء مثيل لنوع الاتحاد 0 | 1 | 2 | 3 | 5 | 6 | 7 | ... .

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

RyanCavanaugh أنواع الطرح؟ 🌞

الأنواع السلبية ، اكتب النفي.

أي شيء ما عدا سلسلة:

type NotAString = !string;

أي رقم ما عدا الصفر:

type NonZeroNumber = number & !0;

يتم تغطية أنواع الطرح streamich بواسطة # 4183

حالة الاستخدام الخاصة بي هي: أرغب في كتابة معامل كـ 0 أو رقم موجب (إنه فهرس مصفوفة).

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

let a = [];
for (let i = 0; i > -10; i -= 1) {
  a[i] = Math.random() * 10;
}

لذلك لا يزال يتعين عليك في النهاية إجراء نفس الفحص

function withItem<T>(items: T[], index: number, f: (x: T) => void) {
  if (items[index]) {
    f(items[index]);
  }
}

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

Frikki هذه الوحدات في فترة زمنية

type Hour =
   | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12
   | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23;

aluanhaddad لكن بدون توقيع دولي:

type UInt = 0..4294967295;

meh ، ماذا عن نوع مثل هذا:

type Factorial<N extends number> = N > 2 ? Factorial<N - 1> * N : N;
type F1 = Factorial<1>; // 1
type F2 = Factorial<2>; // 1 | 2
type F3 = Factorial<3>; // 1 | 2 | 6
type FN = Factorial<number>; // 1 | 2 | 6 | ... 

استخدام عامل تشغيل * من تعليق @ aleksey-bykov:

type char = 0..255;
type word = char ** 2;
type int = word ** 2;
type bigint = int ** 2;

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

streamich ، بعض التعليقات:

  • قد يكون استخدام المصطلحات char ، word ، وما إلى ذلك مربكًا لأن الأشخاص من اللغات الأخرى قد لا يدركون الفرق بين التعريف الثابت وسلوك وقت التشغيل.
  • لا تأخذ الصيغة المقترحة للحد الأدنى في الاعتبار - ماذا لو كانت غير صفرية؟
  • سأكون حذرًا بشأن اختيار عامل الأسي لاستخدامه في سياق محيط / نوع ، نظرًا لأنه تمت إضافته بالفعل إلى ES2016.

دعنا فقط نجعل نظام الكتابة مكتملًا ونستمتع بمشكلة التوقف حيث وصلنا إلى Ctrl + Shift + B

@ aleksey-bykov بالتأكيد تتذكر هذه القضية الجميلة 😀

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

هذه الوحدات [الوحدات الزمنية] موجودة في فاصل زمني محصور بدرجة كافية بحيث يكون من العملي ومن الصعب للغاية كتابتها يدويًا.

https://github.com/Microsoft/TypeScript/issues/15480#issuecomment -349270853

الآن ، افعل ذلك بالمللي ثانية: غمزة:

هل هذه القضية ميتة؟

Palid يتم [Needs Proposal] لذلك أشك في ذلك.

بقدر ما كانت المناقشة ممتعة ، فشل معظمنا في تقديم حالات استخدام واقعية مقنعة.

راجع https://github.com/Microsoft/TypeScript/issues/15480#issuecomment -324152700

__استخدم حالات:__

  1. يمكنك تحديد أنواع int دقيقة مثل تلك المجموعة الفرعية من TypeScript التي يمكن تجميعها إلى WebAssembly أو بعض الأهداف الأخرى.

  2. حالة استخدام أخرى هي UInt8Array ، Int8Array ، Uint16Array ، إلخ .. ، عندما تقرأ أو تكتب البيانات من تلك TypeScript يمكن أن تتحقق من وجود أخطاء.

const ab = new ArrayBuffer(1e3);
const uint8 = new UInt8Array(ab);

uint8[0] = 0xFFFFFFFF; // TSError: Number too big!
  1. لقد ذكرت بعض حالات الاستخدام في OP الخاص بي.

  2. إذا قمت بتنفيذ هذا ، فسيقوم مجتمع TypeScript بملايين حالات الاستخدام.

لدي حالة استخدام مضحكة حقًا ، قد تكون في الواقع أقرب إلى أي شيء آخر مقترح هنا.
تأخذ API عددًا صحيحًا بين بعض النطاقات (في حالتي 5-30) ونحتاج إلى تنفيذ SDK لذلك.
الكتابة بالأسفل يدويًا مملة (على الرغم من أنها يمكن أن تكون آلية نوعًا ما) لـ 25 قيمة ، ولكن ماذا عن المئات أو الآلاف؟
type X = 5 | 6 | 7 | 8 | 9 | 10 ... | 30

Palid هذه هي الحالة الأفضل والأكثر وضوحًا التي رأيتها لهذه الميزة.

إذا تم تعريف نطاق مثل 5..30 على أنه سكر نحوي لـ 5 | 6 | 7 ... | 30 (أو وظائف مماثلة له) أراهن أن هذا سيكون فوزًا سهلاً. هنا يمثل مجموعة منفصلة من الأعداد الصحيحة.

ربما يمكن تحديد النطاق المستمر (على عكس المنفصل) باستخدام رقم بنقطة - 5.0..30.0 .

كنت أفكر في الواقع في البحث في Typescript لتنفيذ حماية الكتابة لـ
https://www.npmjs.com/package/memory-efficient-object

سيجعل وجود الكتابة على النطاق من السهل اكتشاف تجاوز الذاكرة المحتمل في وقت فحص النوع

يبدو أننا لا نحتاج حقًا إلى توسيع الاتحاد ولكننا أشبه بتعريف النطاق الشامل / الحصري الذي يقارن النطاق فقط

type TTerminalColors = int & [0,15];
// int && v >= 0 && v <= 15

سيكون هذا مفيدًا جدًا عند التحكم في المحركات (باستخدام Johnny-Five ، على سبيل المثال) ، حيث تتراوح السرعة من 0-255.

حالة استخدام أخرى: أنا أقوم بتطبيق برنامج رسم قائم على Canvas باستخدام TypeScript وأود أن يكون لدي فحص على التعتيم (والذي يجب أن يكون رقمًا بين 0.0 و 1.0).

مجرد التفكير ... لتنفيذ ذلك بشكل صحيح ، عليك فعلاً بذل كل الجهود:

  • دعم وظائف الحرس نوع وقت التشغيل
  • اكتب تضييقًا لشروط مثل x <= 10
  • دعم النطاقات string | number (نظرًا لأن x == 5 هو true مقابل x === "5" ) بالإضافة إلى نطاقات الأرقام فقط
  • من المحتمل أيضًا دعم نوع int (والذي سيكون نوعًا فرعيًا من number ) ، ودعم النطاقات int فقط و string | int . اكتب أيضًا تضييقًا لتعبيرات مثل x|0

فكرة رائعة ، ولكن سيكون هناك الكثير لتغطيته!

ربما لا نحتاج إلى حارس من نوع وقت التشغيل. بدلا من ذلك يمكننا جعل وقت التجميع حراسة على طول الطريق

سيحتاج هذا إلى تنبؤ تفصيلي بالفرع بدلاً من ذلك

افترض

type TTerminalColors = int & [0,15];

function A(color: TTerminalColors):void
{

}

A(15); // OK
var x = 15;
A(x); // OK

function B(value: int) : void
{
    A(value); // ERROR!!!
    if(value >= 0 && value <= 15)
        A(value); // OK, because we check that it is in the range of TTerminalColors
}

function C(value: int) : void
{
    if(value < 0)
        value = 0;
    if(value > 15)
        value = 15;

    A(value); // OK, because is clamped. But maybe too hard to implemented
}

function ClampInt(value: int): TTerminalColors
{
    if(value >= 0 && value <= 15)
        return value; // Same as B(int)

    if(value > 15)
        return 15;

    return 0;
}

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

export const ajax: (config: AjaxOptions) => void;

type Callback = Success | Error;
type Success = (data: any, statusText: string, xhr: XMLHttpRequest) => void;
type Error = (xhr: XMLHttpRequest, statusText: string) => void;

interface AjaxOptions {
  // ...
  statusCode: { [code: number]: Callback | Callback[] },
  // ...
}

سيكون من الجيد أن تكون قادرًا على تقييد المفاتيح في الخيار statusCode بحيث يمكن في وقت الترجمة تحديد ما إذا كان رمز الحالة يتوافق مع رمز النجاح أو الخطأ:

interface AjaxOptions {
  // ...
  statusCode: {
    200..300: Success,
    400..600: Error
  },
  // ...
}

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

if (val >= range.start && val < range.end) {
  return match;
} else {
  return no_match;
}

ربما يمكننا أخذ ورقة من كتاب روبي واستخدام .. للنطاقات الشاملة ( [start, stop] ) و ... للنطاقات غير الشاملة ( [start, stop) ).

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

هناك حالة استخدام أخرى تتمثل في كتابة التحقق من قيم Lat / Lon.

يمكن تغطية ذلك من خلال آلية التحقق العامة رقم 8665 (لست متأكدًا من سبب إغلاقها كنسخة مكررة):

type TTerminalColors (n: number) => Math.floor(n) == n && n >= 0 && n <= 15;
type TRgbColorComponent (n: number) => Math.floor(n) == n && n >= 0 && n <= 255;
type TUInt (n: number) => n >= 0 && n <= 0..4294967295;

أو تؤخذ مع # 4639 ، وبافتراض أن نوع العدد الصحيح بدون إشارة معرف على أنه uint :

type TTerminalColors (n: uint) => n <= 15;
type TRgbColorComponent (n: uint) => n <= 255;

حالة الاستخدام الخاصة بي هي إحداثيات عالمية. أريد كتابة خطوط الطول والعرض بحيث يقعان فقط ضمن النطاقات المحددة (-90 إلى 90 و -180 إلى 180).

تعديل: خطوط الطول والعرض لها نطاق سلبي

حالة الاستخدام الخاصة بي هي تنفيذ مصفوفات ذات حجم ثابت حيث يكون الحجم معلمة.

على سبيل المثال ، أريد تحديد نوع لمصفوفات سلاسل بطول 2.

let d: FixedSizeArray<2, string>;
d = [ 'a', 'b' ]; // ok
d = [ 'a' ]; // type error
d = [ 'a', 'b', 'c' ]; // type error
d[0] = 'a1'; // ok
d[1] = 'b1'; // ok
d[2] = 'c1' // type error

باستخدام الإصدار الحالي من TS ، من الممكن تحديد شيء قريب جدًا من "المواصفات" أعلاه. المشكلة الرئيسية هي وصول الأعضاء: على سبيل المثال ، يُرجع d[1] = 'b1' خطأ في النوع حتى لو كان صحيحًا. لتجنب الخطأ ، يجب تجميع قائمة جميع المؤشرات القانونية يدويًا في تعريف FixedSizeArray ، وهو أمر ممل.

إذا كان لدينا عامل تشغيل range مشابه لعامل التشغيل keyof ، يجب أن يحل تعريف النوع التالي المشكلة.

type FixedSizeArray<U extends number, T> = {
    [k in range(U)]: T;
} & { length: U };

حيث range(N) هو اختصار لـ range(0,N) .

بالنظر إلى القيم الحرفية العددية (الطبيعية) M ، N مع M <N ،

type r = range(M, N); 

يعادل

type r = M | M+1 | ... | N-1

ما قد يكون أكثر عمومية هو إذا كان بإمكاننا تحديد لامدا المسند واستخدامها كأنواع. مثال:

predicate mypredicate = (x) => x > 1 && x < 10 
let x: mypredicate = 11 // not ok
let x: mypredicate = 5 // ok

المحترف سيكون معقدًا منخفضًا في بناء الجملة لأن Lambdas متوفرة بالفعل ، كل ما نحتاجه هو القدرة على استخدامها كأنواع ، والتحقق من الكتابة محدد بالطباعة على أي حال (مع الأخذ في الاعتبار فلسفة "superset to Javascript")
العيب هو أن تعقيد المسند سيحدد أداء الأدوات لتقديم التغذية الراجعة.

يمكن أن يكون التأكد من انتماء رقم / حرف إلى تسلسل حسابي أمرًا جيدًا للاستخدام الشائع:

// a is the starting element d is the difference between two elements and L is the last element
const belongsToAP = (a, d, L) => {
  return (x) => {
    if(x < a || x > L) return false
    let n = ((x-a)/d) + 1
    if(Number.isInteger(n)) return true
    return false
  }
}

سيسمح لنا هذا بإجراء فحوصات كتابة مثل:
المسند ينتمي إلى MyAP = ينتمي إلى AP (1،1 ، 10)

let x : belongsToMyAP = 5 // ok
let y : belongsToMyAP = 7.2 // not ok

يمكن توسيع هذا ليشمل الشخصيات أيضًا.

Kasahs تم اقتراح شيء مشابه لهذا بالفعل في # 8665.

إلقاء الكثير في حالة استخدام "عكس واجهة برمجة التطبيقات". تأخذ نقطة نهاية REST التي أكتب لها دالة مجمعة عددًا صحيحًا في النطاق 1..1000 كوسيطة. يولد خطأ إذا كان الرقم لا يفي بهذا القيد.

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

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

// Syntax: x..y for an inclusive integer range.

let x: 0..10 = randomNumber(0, 10);
let y = x + 2; // Can deduce that y: 2..12.

هذا جيد عند التخصيص لمتغير جديد ، لكن ماذا عن الطفرة؟

let x: 0..10 = randomNumber(0, 10);
x += 2; // Error: 2..12 is not assignable to type 0..10 (upper bound is out of range).

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

let x = randomNumber(0, 10) as number; // If randomNumber doesn't return a type assigable to number
// this will be an error, but it would still be annoying to have to sprinkle "as number"
// expressions everywhere.

وإذا تجاهلناها ، فسنحصل على الخطأ التالي:

function logNumber0To10 (n: 0..10): void {
    console.log(n);
}

let x: 0..10 = randomNumber(0, 10);
x += 2; // Because we're ignoring mutations, x: 0..10, but the runtime value could be 11 or 12,
// which are outside the specified range...
logNumber0To10(x); // ...which means we lose type safety on this call.

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

وماذا عن الوظائف والأدوية التي يحددها المستخدم؟

// How to define this return type, seeing as we can't do math in types?
function increment<L extends number, H extends number> (x: L..H): (L + 1)..(H + 1);

أي أفكار حول كيفية التعامل مع ما سبق؟

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

ماذا عن الطفرة؟

لماذا يجب أن تكون الطفرة مختلفة عن أي مهمة أخرى؟

ها هي محاولتي المتواضعة لتقديم اقتراح. يطلب هذا الاقتراح أيضًا إضافة أنواع أخرى.

  • Infinity النوع

    • فقط القيمة Infinity

  • -Infinity النوع

    • فقط القيمة -Infinity

  • NaN النوع

    • فقط القيمة NaN

  • double النوع

    • جميع قيم number في النطاق [-Number.MAX_VALUE, Number.MAX_VALUE] أو [-1.7976931348623157e+308, 1.7976931348623157e+308]

  • number هو Infinity|-Infinity|NaN|double
  • int النوع

    • نوع فرعي من double

    • جميع قيم number x في النطاق [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER] أو [-9007199254740991, 9007199254740991] و Math.floor(x) === x

    • لذلك ، ستكون قيم 3 و 3.0 من النوع int

  • النوع "الحرفي المحدود"

    • الأمثلة هي 1 ، 3.141 ، 45

    • قد تكون أنواع فرعية من double أو int

  • يشير النوع "GtEq" إلى (>= x)

    • حيث يكون x قيمة حرفية محدودة ، أو Infinity ، أو -Infinity

  • يشير النوع "LtEq" إلى (<= x)

    • حيث يكون x قيمة حرفية محدودة ، أو Infinity ، أو -Infinity

  • يشير النوع "Gt" إلى (> x)

    • حيث يكون x قيمة حرفية محدودة ، أو Infinity ، أو -Infinity

  • يشير النوع "Lt" إلى (< x)

    • حيث يكون x قيمة حرفية محدودة ، أو Infinity ، أو -Infinity


نوع GtEq ؛ (>= x)

  • (>= Infinity) = Infinity
  • (>= -Infinity) = -Infinity|double|Infinity
  • Infinity هو نوع فرعي من (>= [finite-literal])
  • (>= [finite-literal]) هو نوع فرعي من double|Infinity
  • (>= NaN) = never
  • (>= int) = (>= -9007199254740991)
  • (>= double) = (>= -1.7976931348623157e+308)
  • (>= number) = number

نوع جي تي (> x)

  • (> Infinity) = never
  • (> -Infinity) = double|Infinity
  • Infinity هو نوع فرعي من (> [finite-literal])
  • (> [finite-literal]) هو نوع فرعي من double|Infinity
  • (> NaN) = never
  • (> int) = (> -9007199254740991)
  • (> double) = (> -1.7976931348623157e+308)
  • (> number) = number

نوع LtEq ؛ (<= x)

  • (<= Infinity) = -Infinity|double|Infinity
  • (<= -Infinity) = -Infinity
  • -Infinity هو نوع فرعي من (<= [finite-literal])
  • (<= [finite-literal]) هو نوع فرعي من -Infinity|double
  • (<= NaN) = never
  • (<= int) = (<= 9007199254740991)
  • (<= double) = (<= 1.7976931348623157e+308)
  • (<= number) = number

نوع اللفتنانت (< x)

  • (< Infinity) = -Infinity|double
  • (< -Infinity) = never
  • -Infinity هو نوع فرعي من (< [finite-literal])
  • (< [finite-literal]) هو نوع فرعي من -Infinity|double
  • (< NaN) = never
  • (< int) = (< 9007199254740991)
  • (< double) = (< 1.7976931348623157e+308)
  • (< number) = number

أنواع الميدان

لاحظ أنه بينما يمكننا كتابة أشياء مثل (>= Infinity) ، (> number) ، إلخ ،
النوع الناتج ليس نوع نطاق ؛ إنها مجرد أسماء مستعارة لأنواع أخرى.

نوع النطاق هو أحد ،

  • (>= [finite-literal])
  • (> [finite-literal])
  • (<= [finite-literal])
  • (< [finite-literal])

نسمح باستخدام بناء جملة مثل (> number) وما شابه ذلك للاستخدام في الأدوية الجنيسة.


أنواع الاتحاد GtEq / Gt

عند أخذ اتحاد نوعي GtEq / Gt ، يكون النوع الذي يحتوي على قيم "أكثر" هو النتيجة ،

  • (>= [finite-literal-A]) | (>= [finite-literal-B]) = ...

    • إذا كان [finite-literal-A] >= [finite-literal-B] ، فإن النتيجة هي (>= [finite-literal-B])

    • وإلا فإن النتيجة هي (>= [finite-literal-A])

    • على سبيل المثال ، (>= 3) | (>= 5.5) = (>= 3) لأن (>= 3) هو نوع ممتاز من (>= 5.5)

  • (>= [finite-literal-A]) | (> [finite-literal-B]) = ...

    • إذا كان [finite-literal-A] == [finite-literal-B] ، فإن النتيجة هي (>= [finite-literal-A])

    • إذا كان [finite-literal-A] > [finite-literal-B] ، فإن النتيجة هي (> [finite-literal-B])

    • وإلا فإن النتيجة هي (>= [finite-literal-A])

  • (> [finite-literal-A]) | (> [finite-literal-B]) = ...

    • إذا كان [finite-literal-A] >= [finite-literal-B] ، فإن النتيجة هي (> [finite-literal-B])

    • وإلا فإن النتيجة هي (> [finite-literal-A])

    • على سبيل المثال ، (> 3) | (> 5.5) = (> 3) لأن (> 3) هو نوع ممتاز من (> 5.5)

أيضا،

  • (>= A|B) = (>= A) | (>= B)
  • (> A|B) = (> A) | (> B)

    • على سبيل المثال ، (> 4|3) = (> 4) | (> 3) = (> 3)

    • على سبيل المثال ، (> number|3) = (> number) | (> 3) = number | (> 3) = number

أنواع الاتحاد LtEq / Lt

عند أخذ اتحاد نوعين من LtEq / Lt ، يكون النوع الذي يحتوي على قيم "أكثر" هو النتيجة ،

  • (<= [finite-literal-A]) | (<= [finite-literal-B]) = ...

    • إذا كان [finite-literal-A] <= [finite-literal-B] ، فإن النتيجة هي (<= [finite-literal-B])

    • وإلا فإن النتيجة هي (<= [finite-literal-A])

    • على سبيل المثال ، (<= 3) | (<= 5.5) = (<= 5.5) لأن (<= 5.5) هو نوع ممتاز من (<= 3)

  • (<= [finite-literal-A]) | (< [finite-literal-B]) = ...

    • إذا كان [finite-literal-A] == [finite-literal-B] ، فإن النتيجة هي (<= [finite-literal-A])

    • إذا كان [finite-literal-A] < [finite-literal-B] ، فإن النتيجة هي (< [finite-literal-B])

    • وإلا فإن النتيجة هي (<= [finite-literal-A])

  • (< [finite-literal-A]) | (< [finite-literal-B]) = ...

    • إذا كان [finite-literal-A] <= [finite-literal-B] ، فإن النتيجة هي (< [finite-literal-B])

    • وإلا فإن النتيجة هي (< [finite-literal-A])

    • على سبيل المثال ، (< 3) | (< 5.5) = (< 5.5) لأن (< 5.5) هو نوع ممتاز من (< 3)

أيضا،

  • (<= A|B) = (<= A) | (<= B)
  • (< A|B) = (< A) | (< B)

    • على سبيل المثال ، (< 4|3) = (< 4) | (< 3) = (< 4)

    • على سبيل المثال ، (< number|3) = (< number) | (< 3) = number | (< 3) = number


تقاطع أنواع GtEq / Gt

عند أخذ تقاطع نوعي GtEq / Gt ، يكون النوع الذي يحتوي على قيم "أقل" هو النتيجة ،

  • (>= [finite-literal-A]) & (>= [finite-literal-B]) = ...

    • إذا كان [finite-literal-A] >= [finite-literal-B] ، تكون النتيجة (>= [finite-literal-A])

    • وإلا فإن النتيجة هي (>= [finite-literal-B])

    • على سبيل المثال ، (>= 3) & (>= 5.5) = (>= 5.5) لأن (>= 5.5) هو نوع فرعي من (>= 3)

  • (>= [finite-literal-A]) & (> [finite-literal-B]) = ...

    • إذا كان [finite-literal-A] == [finite-literal-B] ، فإن النتيجة هي (> [finite-literal-B])

    • إذا كان [finite-literal-A] > [finite-literal-B] ، فإن النتيجة هي (>= [finite-literal-A])

    • وإلا فإن النتيجة هي (> [finite-literal-B])

  • (> [finite-literal-A]) & (> [finite-literal-B]) = ...

    • إذا كان [finite-literal-A] >= [finite-literal-B] ، فإن النتيجة هي (> [finite-literal-A])

    • وإلا فإن النتيجة هي (> [finite-literal-B])

    • على سبيل المثال ، (> 3) & (> 5.5) = (> 5.5) لأن (> 5.5) هو نوع فرعي من (> 3)

أنواع التقاطع LtEq / Lt

عند أخذ تقاطع نوعين من LtEq / Lt ، يكون النوع الذي يحتوي على قيم "أقل" هو النتيجة ،

  • (<= [finite-literal-A]) & (<= [finite-literal-B]) = ...

    • إذا كان [finite-literal-A] <= [finite-literal-B] ، فإن النتيجة هي (<= [finite-literal-A])

    • وإلا فإن النتيجة هي (<= [finite-literal-B])

    • على سبيل المثال ، (<= 3) & (<= 5.5) = (<= 3) لأن (<= 3) هو نوع فرعي من (<= 5.5)

  • (<= [finite-literal-A]) & (< [finite-literal-B]) = ...

    • إذا كان [finite-literal-A] == [finite-literal-B] ، فإن النتيجة هي (< [finite-literal-B])

    • إذا كان [finite-literal-A] < [finite-literal-B] ، فإن النتيجة هي (<= [finite-literal-A])

    • وإلا فإن النتيجة هي (< [finite-literal-B])

  • (< [finite-literal-A]) & (< [finite-literal-B]) = ...

    • إذا كان [finite-literal-A] <= [finite-literal-B] ، فإن النتيجة هي (< [finite-literal-A])

    • وإلا فإن النتيجة هي (< [finite-literal-B])

    • على سبيل المثال ، (< 3) & (< 5.5) = (< 3) لأن (< 3) هو نوع فرعي من (< 5.5)


استخدم حالات

  • للتأكد بشكل ثابت من إمكانية احتواء عدد صحيح في نوع بيانات MySQL UNSIGNED INT ،

    //TODO Propose numeric and range sum/subtraction/multiplication/division/mod/exponentiation types?
    function insertToDb (x : int & (>= 0) & (<= 4294967295)) {
      //Insert to database
    }
    
  • للتأكد بشكل ثابت من طول سلسلة معينة ،

    function foo (s : string & { length : int & (>= 1) & (<= 255) }) {
      //Do something with this non-empty string that has up to 255 characters
    }
    
  • للتأكد بشكل ثابت من أن كائن يشبه المصفوفة له قيمة length المناسبة ،

    function foo (arr : { length : int & (>= 0) }) {
      //Do something with the array-like object
    }
    
  • للتأكد بشكل ثابت من حصولنا على أرقام محدودة فقط ،

    function foo (x : double) {
      //`x` is NOT NaN|Infinity|-Infinity
    }
    
  • للتأكد بشكل ثابت من وجود فهارس المصفوفة؟

    function foo (arr : { [index : int & (>= 0) & (< 10)] : string }) {
      console.log(arr[0]); //OK
      console.log(arr[1]); //OK
      console.log(arr[2]); //OK
      console.log(arr[9]); //OK
      console.log(arr[10]); //Error
    }
    

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

[يحرر]
يمكنك إعادة تسمية double وتسميته float لكنني اعتقدت أن double يمثل بدقة أكبر أن هذا رقم فاصلة عائمة مزدوج الدقة.

[يحرر]

تم تغيير بعض الأنواع إلى never .

هل من الممكن أن يكون لديك مترجم قام بتحليل التدفق؟

افترض أن هناك هذه الوظيفة

function DoSomething(x : int & (>= 0) & (< 10)){
   // DoSomething
}

function WillError(x : int){
    DoSomething(x); // error; x is not >= 0 & < 10
}

function WillNotError(x : int){
    if(x >= 0 && x < 10)
        DoSomething(x); // not error by flow analysis
}

حالة استخدام أخرى: لدي إدخال رقم إلى دالة تمثل نسبة مئوية. أريد تحديد القيم لتكون بين 0 و 1.

لقد قمت بتشغيل [...Array(256)].map((_,i) => i).join("|") للتوصل إلى أبشع تعريف للنوع حتى الآن

الأعداد الصحيحة غير السالبة والأعداد الصغيرة ممكنة:

type ArrayT<T> = T extends (infer P)[] ? P : never;
type A = ArrayT<Range<5, 10>>;//5|6|7|8|9|10

النطاق: https : //github.com/kgtkr/typepark/blob/master/src/list.ts

تقصد بـ "الأرقام الصغيرة" بدون استخدام المرحلة 3 BigInt ؟

Mouvedia رقم يناسب حد العودية للمترجم

ربما استخدم .. للأعداد الصحيحة و ... للعوامات.

أود أن أقول أن .. يجب أن يعني نطاقًا شاملاً و ... حصريًا. تمامًا كما هو الحال في روبي على سبيل المثال. راجع http://rubylearning.com/satishtalim/ruby_ranges.html

لست من محبي استخدام فترة واحدة للتمييز بين النطاقات الشاملة والحصرية

هل يمكنني القفز؟ أعتقد أن هذه الميزة التي يُنظر إليها على أنها تحسين فردي لمواصفات النوع ليس من المحتمل أن تكون أفضل طريقة للذهاب هنا.

type range = 1:2:Infinity // 1, 3, 5… Infinity

هذه هي الطريقة التي تحدد بها النطاق الرقمي في العديد من منصات 4gl - خاصة المنصات الموجهة نحو المصفوفة.

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

إذن النطاق المتسلسل هو هذا:

type decmials = 1:10 // like 1 .. 10 with all decimals in between

وإذا كانت مجرد الأعداد الصحيحة:

type integers = 1:1:10 // 1, 2, 3, 4, 5, 6, 7, 8, 10

// OR

type integers = number.integers<1:10>

إذا أردنا أن نلعب مع بناء الجملة:

type something = 1::10 // whatever use cases need today

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

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

من المثير للاهتمام أن تكون قادرًا على تحديد أحجام "الخطوة" في نوع النطاق.

بخلاف نطاقات "الفاصلة العائمة" (بدون حجم خطوة ثابت) و "عدد صحيح" (حجم الخطوة 1 ، يبدأ من عدد صحيح) ، لم أصادف أبدًا حالة استخدام حقيقية لنطاقات ذات أحجام خطوات أخرى.

لذا ، من المثير أن تسمع عن كونها شيئًا في مكان آخر. الآن ، سأحتاج إلى التعرف على 4gl لأنني لم أسمع بها من قبل.


القدرة على تحديد فترة نصف مفتوحة مفيدة للكائنات التي تشبه المصفوفة. شيء مثل،

interface SaferArray<T, LengthT extends integer & (>= 0)> {
    length : LengthT;
    [index in integer & (>= 0) & (< LengthT)] : T
}

إذا كان لدينا نطاقات شاملة فقط ، فسنحتاج إلى (<= LengthT - 1) لكن هذا أقل أناقة

هل يمكنني القفز؟ أعتقد أن هذه الميزة التي يُنظر إليها على أنها تحسين فردي لمواصفات النوع ليس من المحتمل أن تكون أفضل طريقة للذهاب هنا.

type range = 1:2:Infinity // 1, 3, 5… Infinity

هذه هي الطريقة التي تحدد بها النطاق الرقمي في العديد من منصات 4gl - خاصة المنصات الموجهة نحو المصفوفة.

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

إذن النطاق المتسلسل هو هذا:

type decmials = 1:10 // like 1 .. 10 with all decimals in between

وإذا كانت مجرد الأعداد الصحيحة:

type integers = 1:1:10 // 1, 2, 3, 4, 5, 6, 7, 8, 10

// OR

type integers = number.integers<1:10>

إذا أردنا أن نلعب مع بناء الجملة:

type something = 1::10 // whatever use cases need today

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

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

يبدو Hmm بالطريقة التي تم التعامل بها في Haskell. وأعتقد أن هذا أمر جيد. ستسمح المولدات بالتقييم البطيء أيضًا.

ليس الأمر كما لو أنه لا يمكنك الحصول على تقييم كسول مع أي بناء جملة آخر = x

من المثير للاهتمام أن تكون قادرًا على تحديد أحجام "الخطوة" في نوع النطاق.

أفكر في النطاق على أنه بداية ونهاية وزيادة. لذلك إذا تم تقريب الزيادة إلى النهاية تمامًا ، فهي شاملة لذلك. يمكن أن تكون فهارس المصفوفة وأطوالها كوظائف النطاق مصفوفة 0: 1: 9 بها 10 خطوات فهرس (طول). لذلك يمكن أن يكون النطاق هنا ملائمًا integer & 0:9 لنظام الكتابة الذي يمكن أن يستنتج بسهولة أكبر من الجمع بين هذين التعبيرين.

4GL هي حقًا تسمية عامة ، بالنسبة لي كانت في الغالب MatLab

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

لأنه ، بدلاً من استخدام "length" كمعامل نوع فقط ، فأنت بحاجة الآن إلى length و max index. ويجب أن يكون الحد الأقصى للمؤشر مساويًا للطول -1.

ومرة أخرى ، لا يمكنك التحقق من ذلك ما لم تقم بتنفيذ العمليات الحسابية على مستوى الاختراق

AnyhowStep كنت أفكر في أفضل طريقة لتأطير

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

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

أرقام موجبة فقط.

declare let x : (> 0);
x = 0.1; //OK
x = 0.0001; //OK
x = 0.00000001; //OK
x = 0; //Error
x = 1; //OK
x = -1; //Error

مع النطاقات الشاملة فقط ،

declare let x : epsilon:epsilon:Infinity; //Where epsilon is some super-small non-zero, positive number

الأرقام الموجبة ، باستثناء Infinity ،

declare let x : (> 0) & (< Infinity);

مع النطاقات الشاملة فقط ،

const MAX_FLOAT : 1.7976931348623157e+308 = 1.7976931348623157e+308;
declare let x : epsilon:epsilon:MAX_FLOAT;

أيضًا ، لا علاقة له بالمناقشة الحالية ولكن إليك سبب آخر لأني أريد حقًا أنواع النطاقات الرقمية.

الكثير من عملي اليومي هو مجرد تجميع مجموعة من أنظمة البرامج المختلفة معًا. والكثير من هذه الأنظمة لها متطلبات مختلفة للبيانات التي لها نفس المعنى.

على سبيل المثال ، (systemA ، القيمة بين 1 و 255) ، (systemB ، القيمة بين 3 و 73) ، إلخ.
على سبيل المثال (systemC. string length 7-88)، (systemD، string length 9-99)، (systemE، string length 2-101)، إلخ.

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

انا انسان فقط. أنا أرتكب أخطاء. لا أدرك أن البيانات لا يمكن رسمها في بعض الأحيان. فحوصات النطاق تفشل.

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


على سبيل المثال ، كان لدي موقف حيث كان لواجهة برمجة التطبيقات التي كنت أستخدمها حد طول سلسلة يبلغ 10 كيلو بايت لجميع قيم السلسلة. حسنًا ، لم يكن لدي أي طريقة لإخبار TypeScript بالتحقق من أن جميع السلاسل التي تنتقل إلى API هي <= 10k string length.

كان لدي خطأ في وقت التشغيل بدلاً من خطأ تجميع لطيف حيث يمكن أن ينتقل TS ،

`string` is not assignable to `string & { length : (<= 10000) }`

AnyhowStep آمل أن تقدر أن هدفي هو فقط التأكد من أنه إذا كان هناك شيء يسمى "النطاق كنوع رقم" يتماشى ببساطة مع التوقعات الشائعة للمستخدم الأكثر تقليدية (أي شخص ينتقل إلى TS ويتساءل لماذا يؤكد النطاق على الشمولية خلال الفاصل الزمني)

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

إذن في أي مكان آخر يكون إصلاح مثل هذه المشكلة منطقيًا للجميع ، كل ما أتساءل عنه في هذه المرحلة ، الأفكار؟

أنا أتعامل مع واجهة برمجة تطبيقات تمرر مصفوفات البايت ، لذا أود تحديد نوع البايت:

type byte = 0x00..0xFF
type bytes = byte[]

سيكون هذا مفيدًا أيضًا عند العمل مع Uint8Array .

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

TL ؛ DR ،
يمكن أن تعمل أنواع الميدان الآن

interface CompileError<_ErrorMessageT> {
    readonly __compileError : never;
}
///////////////////////////////////////////////
type PopFront<TupleT extends any[]> = (
    ((...tuple : TupleT) => void) extends ((head : any, ...tail : infer TailT) => void) ?
    TailT :
    never
);
type PushFront<TailT extends any[], FrontT> = (
    ((front : FrontT, ...tail : TailT) => void) extends ((...tuple : infer TupleT) => void) ?
    TupleT :
    never
);
type LeftPadImpl<TupleT extends any[], ElementT extends any, LengthT extends number> = {
    0 : TupleT,
    1 : LeftPad<PushFront<TupleT, ElementT>, ElementT, LengthT>
}[
    TupleT["length"] extends LengthT ?
    0 :
    1
];
type LeftPad<TupleT extends any[], ElementT extends any, LengthT extends number> = (
    LeftPadImpl<TupleT, ElementT, LengthT> extends infer X ?
    (
        X extends any[] ?
        X :
        never
    ) :
    never
);
type LongerTuple<A extends any[], B extends any[]> = (
    keyof A extends keyof B ?
    B :
    A
);

///////////////////////////////////////////////////////
type Digit = 0|1|2|3|4|5|6|7|8|9;
/**
 * A non-empty tuple of digits
 */
type NaturalNumber = Digit[];

/**
 * 6 - 1 = 5
 */
type SubOne<D extends Digit> = {
    0 : never,
    1 : 0,
    2 : 1,
    3 : 2,
    4 : 3,
    5 : 4,
    6 : 5,
    7 : 6,
    8 : 7,
    9 : 8,
}[D];

type LtDigit<A extends Digit, B extends Digit> = {
    0 : (
        B extends 0 ?
        false :
        true
    ),
    1 : false,
    2 : LtDigit<SubOne<A>, SubOne<B>>
}[
    A extends 0 ?
    0 :
    B extends 0 ?
    1 :
    2
];


//false
type ltDigit_0 = LtDigit<3, 3>;
//true
type ltDigit_1 = LtDigit<3, 4>;
//false
type ltDigit_2 = LtDigit<5, 2>;


/**
 * + Assumes `A` and `B` have the same length.
 * + Assumes `A` and `B` **ARE NOT** reversed.
 *   So, `A[0]` is actually the **FIRST** digit of the number.
 */
type LtEqNaturalNumberImpl<
    A extends NaturalNumber,
    B extends NaturalNumber
> = {
    0 : true,
    1 : (
        LtDigit<A[0], B[0]> extends true ?
        true :
        A[0] extends B[0] ?
        LtEqNaturalNumberImpl<
            PopFront<A>,
            PopFront<B>
        > :
        false
    ),
    2 : never
}[
    A["length"] extends 0 ?
    0 :
    number extends A["length"] ?
    2 :
    1
];
type LtEqNaturalNumber<
    A extends NaturalNumber,
    B extends NaturalNumber
> = (
    LtEqNaturalNumberImpl<
        LeftPad<A, 0, LongerTuple<A, B>["length"]>,
        LeftPad<B, 0, LongerTuple<A, B>["length"]>
    > extends infer X ?
    (
        X extends boolean ?
        X :
        never
    ) :
    never
);

//false
type ltEqNaturalNumber_0 = LtEqNaturalNumber<
    [1],
    [0]
>;
//true
type ltEqNaturalNumber_1 = LtEqNaturalNumber<
    [5,2,3],
    [4,8,9,2,3]
>;
//false
type ltEqNaturalNumber_2 = LtEqNaturalNumber<
    [4,8,9,2,3],
    [5,2,3]
>;
//true
type ltEqNaturalNumber_3 = LtEqNaturalNumber<
    [5,2,3],
    [5,2,3]
>;
//true
type ltEqNaturalNumber_4 = LtEqNaturalNumber<
    [5,2,2],
    [5,2,3]
>;
//false
type ltEqNaturalNumber_5 = LtEqNaturalNumber<
    [5,1],
    [2,5]
>;
//false
type ltEqNaturalNumber_6 = LtEqNaturalNumber<
    [2,5,7],
    [2,5,6]
>;

type RangeLt<N extends NaturalNumber> = (
    number &
    {
        readonly __rangeLt : N|undefined;
    }
);
type StringLengthLt<N extends NaturalNumber> = (
    string & { length : RangeLt<N> }
);

type AssertStringLengthLt<S extends StringLengthLt<NaturalNumber>, N extends NaturalNumber> = (
    LtEqNaturalNumber<
        Exclude<S["length"]["__rangeLt"], undefined>,
        N
    > extends true ?
    S :
    CompileError<[
        "Expected string of length less than",
        N,
        "received",
        Exclude<S["length"]["__rangeLt"], undefined>
    ]>
);
/**
 * String of length less than 256
 */
type StringLt256 = string & { length : RangeLt<[2,5,6]> };
/**
 * String of length less than 512
 */
type StringLt512 = string & { length : RangeLt<[5,1,2]> };

declare function foo<S extends StringLengthLt<NaturalNumber>> (
    s : AssertStringLengthLt<S, [2,5,6]>
) : void;

declare const str256 : StringLt256;
declare const str512 : StringLt512;

foo(str256); //OK!
foo(str512); //Error

declare function makeLengthRangeLtGuard<N extends NaturalNumber> (...n : N) : (
    (x : string) => x is StringLengthLt<N>
);

if (makeLengthRangeLtGuard(2,5,6)(str512)) {
    foo(str512); //OK!
}

declare const blah : string;
foo(blah); //Error

if (makeLengthRangeLtGuard(2,5,5)(blah)) {
    foo(blah); //OK!
}

if (makeLengthRangeLtGuard(2,5,6)(blah)) {
    foo(blah); //OK!
}

if (makeLengthRangeLtGuard(2,5,7)(blah)) {
    foo(blah); //Error
}

ملعب

يستخدم النوع CompileError<> من هنا ،
https://github.com/microsoft/TypeScript/issues/23689#issuecomment -512114782

النوع AssertStringLengthLt<> هو المكان الذي يحدث فيه السحر

باستخدام الإضافة على مستوى النوع ، يمكنك الحصول على str512 + str512 والحصول على str1024

https://github.com/microsoft/TypeScript/issues/14833#issuecomment -513106939

بالنظر إلى حل AnyhowStep ، أعتقد أن لدي حل مؤقت أفضل. تذكر نوع الحراس ؟:

/**
 * Just some interfaces
 */
interface Foo {
    foo: number;
    common: string;
}

interface Bar {
    bar: number;
    common: string;
}

/**
 * User Defined Type Guard!
 */
function isFoo(arg: any): arg is Foo {
    return arg.foo !== undefined;
}

/**
 * Sample usage of the User Defined Type Guard
 */
function doStuff(arg: Foo | Bar) {
    if (isFoo(arg)) {
        console.log(arg.foo); // OK
        console.log(arg.bar); // Error!
    }
    else {
        console.log(arg.foo); // Error!
        console.log(arg.bar); // OK
    }
}

doStuff({ foo: 123, common: '123' });
doStuff({ bar: 123, common: '123' });

لذا ألق نظرة على الكود التالي:

class NumberRange {
    readonly min: number;
    readonly max: number;
    constructor(min:number, max:number, ) {
        if (min > max) { 
            throw new RangeError(`min value (${min}) is greater than max value (${max})`);
        } else {
            this.min = min;
            this.max = max;
        }
    }
    public isInRange = (num: number, explicit = false): boolean => {
        let inRange: boolean = false;
        if (explicit === false) {
            inRange = num <= this.max && num >= this.min;
        } else {
            inRange = num < this.max && num > this.min;
        }
        return inRange;
    };
}
const testRange = new NumberRange(0, 12);
if(testRange.isInRange(13)){
    console.log('yay')
}else {
  console.log('nope')
}

إنها مجرد فكرة ولكن مع أدوات الحماية من الممكن استخدام النطاقات.

المشكلة هنا أنه لا يمكنك فعل هذا ،

declare const a : number;
declare let b : (>= 5);
const testRange = new NumberRange(12, 20);
if (testRange.isInRange(a)) {
  b = a; //ok
} else {
  b = a; //compile error
}

حراس النوع وحدهم ليسوا حلا مرضيا

أيضًا ، لا يستخدم مثالك حراس الكتابة.


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


أيضًا ، عادةً ما تكون أنواع العلامات والأنواع الاسمية وكائنات القيمة المستخدمة في العناصر الأولية علامة على أن نظام الكتابة ليس معبرًا بشكل كافٍ.

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

https://github.com/microsoft/TypeScript/issues/6579#issuecomment -548249683

يمكنك تحقيق ذلك باستخدام الإصدار الحالي من Typescript:

// internal helper types
type IncrementLength<A extends Array<any>> = ((x: any, ...xs: A) => void) extends ((...a: infer X) => void) ? X : never;
type EnumerateRecursive<A extends Array<any>, N extends number> = A['length'] extends infer X ? (X | { 0: never, 1: EnumerateRecursive<IncrementLength<A>, N> }[X extends N ? 0 : 1]) : never;

// actual utility types
export type Enumerate<N extends number> = Exclude<EnumerateRecursive<[], N>, N>;
export type Range<FROM extends number, TO extends number> = Exclude<Enumerate<TO>, Enumerate<FROM>>;

// usage examples:
type E1 = Enumerate<3>; // hover E1: type E1 = 0 | 1 | 2
type E2 = Enumerate<10>;  // hover E2: type E2 = 0 | 1 | 3 | 2 | 4 | 5 | 6 | 7 | 8 | 9

type R1 = Range<0, 5>; // hover R1: type R1 = 0 | 1 | 3 | 2 | 4
type R2 = Range<5, 11>; // hover R2: type R2 = 10 | 5 | 6 | 7 | 8 | 9

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

تلميحات تحوم رمز vs في التعليقات.

لاحظ أنه يتم فرز الأرقام الموجودة في تلميحات التمرير بشكل عشوائي.

image

للأسف ، لا يعمل إلا ما يصل إلى حوالي 10 عناصر :(

تحرير: يبدو أن Enumerate يمكنه فقط التعامل مع ما يصل إلى 15 العودية (0-14)

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

type PrependNextNum<A extends Array<unknown>> = A['length'] extends infer T ? ((t: T, ...a: A) => void) extends ((...x: infer X) => void) ? X : never : never;
type EnumerateInternal<A extends Array<unknown>, N extends number> = { 0: A, 1: EnumerateInternal<PrependNextNum<A>, N> }[N extends A['length'] ? 0 : 1];
export type Enumerate<N extends number> = EnumerateInternal<[], N> extends (infer E)[] ? E : never;
export type Range<FROM extends number, TO extends number> = Exclude<Enumerate<TO>, Enumerate<FROM>>;

type E1 = Enumerate<40>;
type E2 = Enumerate<10>;
type R1 = Range<0, 5>;
type R2 = Range<5, 34>;

والآن حدث أن تم فرزها بطريقة سحرية ؛).

حالات الاستخدام المعتادة بالنسبة لي تستخدم نطاقات مثل [1, 255] ، [1, 2048] ، [1, 4096] ، [20, 80] ، إلخ. قد يؤدي إنشاء أنواع اتحادات كبيرة إلى جعل TS يفزع / يبطئ . لكن هذه الحلول تعمل بالتأكيد مع نطاقات "أصغر"

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

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

ما زلت لا توجد حلول مثالية على ما أعتقد؟

آمل أن يتم ذلك على النحو التالي بشكل عام (ولكن صعب).

type X => (number >= 50 && number > 60 || number = 70) || (string.startsWith("+"))

(على سبيل المثال ، يمكن استخدام عبارة / وظيفة جافا سكريبت العادية هنا ، باستثناء المتغيرات التي يتم استبدالها بالنوع)

في ما أفهمه ، اللغة الحديثة = منطق عادي + منطق ميتا ، حيث المنطق التلوي = فحص الكود + توليد الكود. تحاول الكثير من الأعمال دمج بناء جملة المنطق الفوقي بطريقة أنيقة.

ربما يمكننا أن نفترض بطريقة أو بأخرى أن أنواع النطاق ليست نوعًا من السكر ، ولكنها مفهوم أساسي؟

// number literal extend `number` despite the fact 
// that `number` is not union of all number literals
type NumberLiteralIsNumber = 5 extends number ? true : false // true

// so if we will define Range (with some non-existing syntax which is clearly should be done in lib internals)
type Range<MIN extends number, MAX extends number> = MAX > MIN ? 5 and 2NaN : MAX === MIN ? MAX : MIN..MAX

// and assume that `number` is Range<-Infinity, +Infinity>
type NumberIsInfinitRange = number extends Range<-Infinity, +Infinity> ?
    Range<-Infinity, +Infinity> extends number ? true : false :
    false // true

// following things will be true
type AnyRangeIsNumber<T extends number, K extends Number> = 
    Range<T, K> extends number ? true : false // true
type NestedRangeIsNumber<T extends number, K extends number, S extends number> =
    Range<T, Range<K, S>> extends number ? true : false // true

لإكمال هذا نحتاج إلى الإعلان عن السلوك في بعض الحالات

  1. Range<T, T> === T حسب التعريف
  2. Range<5, 1> === NaN حسب التعريف
  3. Range<NaN, T> === Range<T, NaN> === NaN حيث لا يمكن أن توجد هذه القيمة
  4. Range<1 | 2, 5> === Range<2, 5> احتمالية وجود عدد أكبر كمعامل من النوع الأول يقيد النطاق ليكون أقصر
  5. Range<1, 4 | 5> === Range<1, 4> احتمالية وجود رقم أقل كمعامل من النوع الثاني يقيد النطاق ليكون أقصر
  6. يجب أن يكون 1.5 extends Range<1, 2> صحيحًا حسب التعريف
  7. Range<Range<A, B>, C> === NaN extends Range<A, B> ? NaN : Range<B, C> يتبع من 5 و 3
  8. Range<A, Range<B, C>> === NaN extends Range<B, C> ? NaN : Range<A, B> يتبع من 6 و 3

والقليل عن النطاقات داخل النطاقات:

type RangeIsInsideRange<T extends Range<any, any>, K extends Range<any, any>> = 
    T extends Range<infer A, infer B> 
        ? K extends Range<infer C, infer D> 
            ? NaN extends Range<A, C> 
                ? false 
                : NaN extends Range<B, D> 
                    ? false 
                    : true 
            : never
        : never

يمكن تعريف أنواع النطاقات الفرعية بطريقة عامة ، كوظيفة مقارنة على مستوى النوع (a: T, b: T) => '<' | '=' | '>' .

... هل هناك اقتراح لاستخدام وظائف JS العادية على مستوى النوع؟

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

مشكلة yudinns هي أن النطاق موجود على الأقل بهذا الاسم ، لذلك قد يكون محيرًا ، راجع: https://developer.mozilla.org/en-US/docs/Web/API/range أيضًا أفتقد الوظيفة حيث يمكنني تحديد سلسلة رقمية مثل <1, n+2> - الخطوة الثانية تبدأ من 1 أو معادلة أكثر تعقيدًا.

من المحتمل أن يصل
التذكرة 3 سنوات.

الاقتراح: نوع السلسلة التي تم التحقق من صحتها من خلال Regex: https://github.com/Microsoft/TypeScript/issues/6579

(سؤال SO ذو الصلة: https://stackoverflow.com/questions/3895478/does-javascript-have-a-method-like-range-to-generate-a-range-within-the-supp)

سيكون من الرائع أيضًا أن تفعل شيئًا مثل number in range سبيل المثال if 1 in 1..2 أو شامل if 1 in 1..=2 الصدأ يتعامل معه جيدًا https://doc.rust-lang.org/reference/ التعبيرات / range-expr.html . هذا من شأنه أن يوفر مساحة كبيرة

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

لقد واجهت هذا في أكثر من مناسبة وجئت إلى هنا للبحث عن طريقة لتحديد نوع يكون كسرًا بين 0 ... 1 - تتم مناقشة هذا على https://news.ycombinator.com/item؟id= 24362658 # 24372935 لأنه لا توجد كلمة صحيحة في اللغة الإنجليزية لتعريف هذه الأنواع من القيم. إذا كان يمكن أن تساعد الكتابة المطبوعة هنا ، فسيكون ذلك ممتازًا ولكن قد يكون من الصعب للغاية فرضه على مستوى النوع.

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

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

نعم UnitInterval هو الاسم الصحيح للنطاق 0 ... 1 لذا فهو مناسب للنوع! لا يزال غير صحيح لتسمية الرقم ، لذا سيكون من الرائع إذا كان هذا متاحًا لوصف الكود الخاص بنا بشكل أكثر دقة باستخدام مثل هذا النوع - كيف يعمل هذا تحت الغطاء في نظام نوع Rust - هل هو مجرد حارس أم؟

إذا لم يكن CantorSpace امتدادًا كبيرًا ، أعتقد أن هذا سيكون تعريفًا عادلًا لـ "نطاق" "كل" الأرقام الحقيقية بين [0, 1] كما يفهمها الكمبيوتر. لا يمكن الاستدلال على تعيين هذه القيم في وقت الترجمة عن طريق الحد الأدنى والأعلى لـ Math.floor أو Math.ceil منذ Math.ceil(0) === 0 ، و Math.floor(1) === 1 وهو مؤسف.

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

أو ربما تستخدم 1~~3 للأعداد الصحيحة و 0.1~0.5 للعوامات؟

~ مأخوذ بالفعل من قبل عامل التشغيل أحادي البت NOT .

لماذا نحتاج إلى التحدث عن تلك العوامات اللعينة ، المشكلة التي نواجهها هي الأعداد الصحيحة. 99.99٪ مشكلة عدد صحيح 0.001٪ مشكلة عائمة.
لا يوجد حل عام ممكن بسهولة ، لذلك دعنا ننتقل إلى نطاق الأرقام 0..10 كما هو موضح في معظم لغات البرمجة ، كما تم تنفيذه بالفعل منذ سنوات.

توقف عن الكلام العائم

ربما يمكننا تقسيم الفاتورة وتوسيع هذا الاقتراح للحالة الدولية:
https://gist.github.com/rbuckton/5fd81582fdf86a34b45bae82d842304c

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

فقط اجعلها بسيطة مع
x..y للأعداد الصحيحة فقط.
سيظهر خطأ في 0.1..2
قد يظهر خطأ أيضًا في 1..2.1
وأي عدد غير صحيح يمكن اكتشافه.
هيك نسخ منطق التنفيذ من
https://en.m.wikibooks.org/wiki/Ada_Programming/Types/range
أو
https://kotlinlang.org/docs/reference/ranges.html
أو
https://doc.rust-lang.org/reference/expressions/range-expr.html
وندعوها اليوم.

✅ لا حاجة لإعادة اختراع العجلة
✅ لا حاجة لحساب العوامات
✅ الاتساق عبر اللغة
✅ اختبار ونسخ كود ثابت قادر من المصادر الحالية

يستحق الأمر الجهد المبذول لحل العوامات ، نظرًا لأن التحقق من النوع المستند إلى "int" أمر تافه للأرقام <1000 من خلال الترميز الثابت لقيمة النوع كوحدة لكل عدد صحيح ثابت 0..1000 ، لذلك نقول أنه "لا داعي" لإعادة -اختراع العجلة "هو نوع من السخرية. لماذا لا تستخدم ما يصلح بالفعل لحالات الاستخدام البسيطة؟

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

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

aMoniker اعتمادًا على تعريف explicit : BigInt

حتى بالنسبة إلى bigint ، هناك حالات استخدام لا يمكن للنقابات حلها مثل التأكد من أن bigint يتناسب مع Bigint موقع / بدون توقيع MySQL

ماذا عن إنشاء نوع نطاق intrinsic لهذه الميزة؟

lib.es5.d.ts (أو lib.es2020.bigint.d.ts لتضمين دعم المهمات)
type GreaterThan<N extends number | bigint> = intrinsic
type GreaterThanOrEqualTo<N extends number | bigint> = GreaterThan<N> | N
type LessThan<N extends number | bigint> = intrinsic
type LessThanOrEqualTo<N extends number | bigint> = LessThan<N> | N

/**
 * prevent `GreaterThan` and `LessThan` from desugaring
 * (in the same way that `number` does _not_ desugar to `-Infinity | ... | Infinity`)
 */
أرض المستخدم
type GreaterThanOrEqualTo2 = GreaterThanOrEqualTo<2>
type LessThan8 = LessThan<8>

type GreaterThanOrEqualTo2_And_LessThan8 = GreaterThanOrEqualTo2 & LessThan8
type LessThan2_Or_GreaterThanOrEqualTo8 = LessThan<2> | GreaterThanOrEqualTo<8> // inverse of `GreaterThanOrEqualTo2_And_LessThan8` (would be nice to be able to just do `Exclude<number | bigint, GreaterThanOrEqualTo2_And_LessThan8>` but that might be wishful thinking)
type LessThan2_And_GreaterThanOrEqualTo8 = LessThan<2> & GreaterThanOrEqualTo<8> // `never`
type GreaterThanOrEqualTo2_Or_LessThan8 = GreaterThanOrEqualTo2 | LessThan8 // `number | bigint`

type RangesAreNumbersOrBigIntsByDefault = LessThan8 extends number | bigint ? true : false // `true` (the user could always narrow this on a per-range basis, e.g. `LessThan8 & number`)
type RangesAcceptUnions = LessThan<7n | 7.5 | 8> // `LessThan<8>`
type RangesAcceptOtherRanges1 = LessThan<LessThan8> // `LessThan8`
type RangesAcceptOtherRanges2 = LessThan<GreaterThanOrEqualTo2> // `number | bigint`
type RangesSupportBeingInAUnion = (-6 | 0.42 | 2n | 2 | 3) | LessThan<2> // `LessThan<2> | 2n | 2 | 3`
type RangesSupportBeingInAnIntersection = (-6 | 0.42 | 2n | 2 | 3) & LessThan<2> // `-6 | 0.42`
type RangesSupportBeingInAUnionWithOtherRanges = LessThan<2> | LessThan8 // `LessThan8`
type RangesSupportBeingInAnIntersectionWithOtherRanges = LessThan<2> & LessThan8 // `LessThan<2>`

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

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

سيكون ما يلي غير سليم بدون نوع int:

const arr: string[] = ['a', 'b', 'c']
const item = arr[1.01]  // Typescript inferred this as string but actually it is undefined
console.log(item)  // Will print undefined, we miss inferred the type

سيؤدي وجود نوع المتابعة إلى منع حدوث هذه المشكلة:

type TUInt = 0..4294967295;

هناك حالة استخدام أخرى لنوع Int32 و Int64 وجه التحديد عندما يبدأ الأشخاص في إضافة تعليقات توضيحية إلى التعليمات البرمجية الخاصة بهم ... سيفتح الباب أمام إمكانية التشغيل البيني بشكل أفضل مع اللغات الأخرى ... كلها تقريبًا مكتوبة بشكل ثابت اللغات لها نوع صحيح: Java ، C # ، C ، Rust ، F # ، Go ... إلخ.

إذا كنت أرغب في استدعاء مكتبة npm مكتوبة بلغة TypeScript من C # على سبيل المثال ، فهناك مكتبات تأخذ تعريفات TypeScript وتقوم بإنشاء واجهة لي في C # ولكن المشكلة هي number type هو float والتي لا يمكن استخدامها لفهرسة مصفوفة في C # بدون غلافها ... إلخ.

حالات استخدام أخرى: أسهل في التحويل بين المقاييس ، وتحسين الأداء ... إلخ

لاحظ أن هناك حالة أخرى يكون فيها هذا مفيدًا وهي التفاعل مع WebAssembly ، والذي يحتوي على أنواع أرقام منفصلة واضحة جدًا (أعتقد أنه تم ذكره باختصار على أنه حالة استخدام C # ، لكنني أردت توضيح أنه ينطبق على نطاق أوسع بكثير من ذلك).

UPD: Nvm ، أرى أنه مذكور في https://github.com/microsoft/TypeScript/issues/15480#issuecomment -365420315 الذي قام Github "بشكل مفيد" بإخفائه كـ "عناصر مخفية".

+1

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

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

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

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

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

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

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