Typescript: اقتراح: نوع سلسلة تم التحقق من صحة Regex

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

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

let fontStyle: 'normal' | 'italic' = 'normal'; // already available in master
let fontColor: /^#([0-9a-f]{3}|[0-9a-f]{6})$/i = '#000'; // my suggestion

من الشائع في JavaScript تخزين قيم الألوان في تدوين css ، كما هو الحال في انعكاس نمط css لعقد DOM أو مكتبات مختلفة تابعة لجهات خارجية.

ماذا تعتقد؟

Literal Types Needs Proposal Suggestion

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

اقتراح تصميم

هناك الكثير من الحالات التي يحتاج فيها المطورون إلى قيمة أكثر تحديدًا ثم مجرد سلسلة ، ولكن لا يمكنهم تعدادها على أنها اتحاد من سلسلة حرفية بسيطة ، مثل ألوان css ورسائل البريد الإلكتروني وأرقام الهواتف والرمز البريدي وملحقات swagger وما إلى ذلك. يستخدم لوصف مخطط كائن JSON له نقش وأنماط خصائص يمكن regex-validated string type و regex-validated string type of index .

الأهداف

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

نظرة عامة نحوية

يتكون تنفيذ هذه الميزة من 4 أجزاء:

نوع Regex تم التحقق من صحته

type CssColor = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
type Email = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i;
type Gmail = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@gmail\.com$/i;

نوع متغير تم التحقق منه من خلال Regex

let fontColor: /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;

ونفس الشيء ، ولكن أكثر قابلية للقراءة

let fontColor: CssColor;

نوع متغير من الفهرس تم التحقق منه من خلال Regex

interface UsersCollection {
    [email: /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i]: User;
}

ونفس الشيء ، ولكن أكثر قابلية للقراءة

interface UsersCollection {
    [email: Email]: User;
}

اكتب حارس لنوع متغير

setFontColorFromString(color: string) {
    fontColor = color;// compile time error
    if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(color)) {
        fontColor = color;// correct
    }
}

ونفس الشيء

setFontColorFromString(color: string) {
    fontColor = color;// compile time error
    if (!(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(color))) return;
    fontColor = color;// correct
}

واستخدام النوع المحدد لقراءة أفضل

setFontColorFromString(color: string) {
    fontColor = color;// compile time error
    if (CssColor.test(color)) {
        fontColor = color;// correct
    }
}

مثل

setFontColorFromString(color: string) {
    fontColor = color;// compile time error
    if (!(CssColor.test(color))) return;
    fontColor = color;// correct
}

اكتب gurard لنوع الفهرس

let collection: UsersCollection;
getUserByEmail(email: string) {
    collection[email];// type is any
    if (/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i.test(email)) {
        collection[email];// type is User
    }
}

مثل

let collection: UsersCollection;
getUserByEmail(email: string) {
    collection[email];// type is any
    if (!(/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i.test(email))) return;
    collection[email];// type is User
}

واستخدام النوع المحدد لقراءة أفضل

let collection: UsersCollection;
getUserByEmail(email: string) {
    collection[email];// type is any
    if (Email.test(email)) {
        collection[email];// type is User
    }
}

مثل

let collection: UsersCollection;
getUserByEmail(email: string) {
    collection[email];// type is any
    if (!(Email.test(email))) return;
    collection[email];// type is User
}

نظرة عامة دلالية

تعيينات

let email: Email;
let gmail: Gmail;
email = '[email protected]';// correct
email = '[email protected]';// correct
gmail = '[email protected]';// compile time error
gmail = '[email protected]';// correct
gmail = email;// obviously compile time error
email = gmail;// unfortunately compile time error too

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

// explicit cast
gmail = <Gmail>email;// correct
// type guard
if (Gmail.test(email)) {
    gmail = email;// correct
}
// another regex subtype declaration
type Gmail = Email & /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@gmail\.com$/i;
gmail = email;// correct

لسوء الحظ ، يجب أيضًا تقييد تعيين متغير string لمتغير regex-validated ، لأنه لا يوجد ضمان في وقت الترجمة بأنه سيتطابق مع regex.

let someEmail = '[email protected]';
let someGmail = '[email protected]';
email = someEmail;// compile time error
gmail = someGmail;// compile time error

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

let someEmail: '[email protected]' = '[email protected]';
let someGmail: '[email protected]' = '[email protected]';
email = someEmail;// correct
gmail = someGmail;// correct

اكتب تضييق الفهارس

للحالات البسيطة من الفهرس regex-validated type انظر النوع gurard لنوع الفهرس .
ولكن قد تكون هناك حالات أكثر تعقيدًا:

type Email = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i;
type Gmail = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@gmail\.com$/i;
interface UsersCollection {
    [email: Email]: User;
    [gmail: Gmail]: GmailUser;
}
let collection: UsersCollection;
let someEmail = '[email protected]';
let someGmail = '[email protected]';
collection['[email protected]'];// type is User
collection['[email protected]'];// type is User & GmailUser
collection[someEmail];// unfortunately type is any
collection[someGmail];// unfortunately type is any
// explicit cast is still an unsafe workaround
collection[<Email> someEmail];// type is User
collection[<Gmail> someGmail];// type is GmailUser
collection[<Email & Gmail> someGmail];// type is User & GmailUser

لا توجد مثل هذه المشكلة في الحروف:

let collection: UsersCollection;
let someEmail: '[email protected]' = '[email protected]';
let someGmail: '[email protected]' = '[email protected]';
collection[someEmail];// type is User
collection[someGmail];// type is User & GmailUser

ولكن بالنسبة للمتغيرات ، فإن الخيار الأفضل هو استخدام حراس الكتابة كما هو الحال في الأمثلة الأكثر واقعية التالية:

getUserByEmail(email: string) {
    collection[email];// type is any
    if (Email.test(email)) {
        collection[email];// type is User
        if (Gmail.test(email)) {
            collection[email];// type is User & GmailUser
        }
    }
    if (Gmail.test(email)) {
        collection[email];// type is GmailUser
    }
}

ولكن إذا استخدمنا تعريفًا أفضل للكتابة Gmail ، فسيكون لها نوع آخر من التضييق:

type Gmail = Email & /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@gmail\.com$/i;
getUserByEmail(email: string) {
    collection[email];// type is any
    if (Email.test(email)) {
        collection[email];// type is User
        if (Gmail.test(email)) {
            collection[email];// type is User & GmailUser
        }
    }
    if (Gmail.test(email)) {
        collection[email];// type is User & GmailUser
    }
}

النقابات والتقاطعات

في الواقع الأنواع الشائعة وأنواع regex-validated مختلفة حقًا ، لذلك نحن بحاجة إلى قواعد حول كيفية التعامل مع اتحاداتهم وتقاطعاتهم.

type Regex_1 = / ... /;
type Regex_2 = / ... /;
type NonRegex = { ... };
type test_1 = Regex_1 | Regex_2;// correct
type test_2 = Regex_1 & Regex_2;// correct
type test_3 = Regex_1 | NonRegex;// correct
type test_4 = Regex_1 & NonRegex;// compile time error
if (test_1.test(something)) {
    something;// type is test_1
    // something matches Regex_1 OR Regex_2
}
if (test_2.test(something)) {
    something;// type is test_2
    // something matches Regex_1 AND Regex_2
}
if (test_3.test(something)) {
    something;// type is Regex_1
} else {
    something;// type is NonRegex
}

علم الوراثة

لا توجد حالات خاصة للأدوية ، لذا يمكن استخدام النوع regex-validated مع الأدوية الجنيسة بنفس الطريقة التي تستخدم بها الأنواع المعتادة.
بالنسبة للأدوية ذات القيود مثل أدناه ، يتصرف النوع regex-validated مثل السلسلة:

class Something<T extends String> { ... }
let something = new Something<Email>();// correct

نظرة عامة على الإصدار

بخلاف الأنواع المعتادة ، يكون لـ regex-validated بعض التأثير على الانبعاث:

type Regex_1 = / ... /;
type Regex_2 = / ... /;
type NonRegex = { ... };
type test_1 = Regex_1 | Regex_2;
type test_2 = Regex_1 & Regex_2;
type test_3 = Regex_1 | NonRegex;
type test_4 = Regex_1 & NonRegex;
if (test_1.test(something)) {
    /* ... */
}
if (test_2.test(something)) {
    /* ... */
}
if (test_3.test(something)) {
    /* ... */
} else {
    /* ... */
}

سيتم تجميعها إلى:

var Regex_1 = / ... /;
var Regex_2 = / ... /;
if (Regex_1.test(something) || Regex_2.test(something)) {
    /* ... */
}
if (Regex_1.test(something) && Regex_2.test(something)) {
    /* ... */
}
if (Regex_1.test(something)) {
    /* ... */
} else {
    /* ... */
}

نظرة عامة على التوافق

لا توجد مشاكل مع هذه الميزة في التوافق ، نظرًا لوجود حالة فقط يمكن أن تكسرها وهي مرتبطة بهذا النوع regex-validated له تأثير على عكس النوع المعتاد ، لذلك هذا رمز TS صالح:

type someType = { ... };
var someType = { ... };

عندما لا يكون الرمز أدناه:

type someRegex = / ... /;
var someRegex = { ... };

لكن الثانية كانت غير صالحة بالفعل ، ولكن لسبب آخر (إعلان النوع كان خاطئًا).
لذا علينا الآن تقييد التصريح عن المتغير بالاسم نفسه للنوع ، في حال كان هذا النوع هو regex-validated .

ملاحظة

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

ال 146 كومينتر

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

المشاكل الرئيسية هي:

  • ليس من الواضح كيف تؤلف هذه بشكل جيد. إذا أردت قائمة مفصولة بفواصل من "cat" ، "dog" ، و "fish" ، فأنا بحاجة إلى كتابة شيء مثل /dog|cat|fish(,(dog|cat|fish))*/ .

    • إذا كان لدي بالفعل أنواع تصف أنواع السلاسل الحرفية لـ "cat" ، "dog "، و "fish" ، كيف يمكنني دمجها في هذا التعبير العادي؟

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

  • تجعل الامتدادات غير القياسية هذا النوع من عدم الدقة.

إجراء +1 ضخم على هذا ، الرمز البريدي ، SSN ، ONet ، العديد من حالات الاستخدام الأخرى لهذا الغرض.

لقد واجهت نفس المشكلة ، وأرى أنها لم يتم تنفيذها بعد ، فربما يكون هذا الحل مفيدًا:
http://stackoverflow.com/questions/37144672/guid-uuid-type-in-typescript

كما اقترح mhegazy سأضع اقتراحي (# 8665) هنا. ماذا عن السماح بوظائف التحقق البسيطة في إقرارات النوع؟ شئ مثل هذا:

type Integer(n:number) => String(n).macth(/^[0-9]+$/)
let x:Integer = 3 //OK
let y:Integer = 3.6 //wrong

type ColorLevel(n:number) => n>0 && n<= 255
type RGB = {red:ColorLevel, green:ColorLevel, blue:ColorLevel};
let redColor:RGB = {red:255, green:0, blue:0}   //OK
let wrongColor:RGB = {red:255, green:900, blue:0} //wrong

type Hex(n:string) => n.match(/^([0-9]|[A-F])+$/)
let hexValue:Hex = "F6A5" //OK
let wrongHexValue:Hex = "F6AZ5" //wrong

سيتم تحديد القيمة التي يمكن أن يقبلها النوع بواسطة نوع معلمة الوظيفة وتقييم الوظيفة نفسه. هذا من شأنه أن يحل # 7982 أيضًا.

rylphs +1 هذا من شأنه أن يجعل TypeScript قويًا للغاية

كيف يعمل النوع الفرعي مع أنواع السلاسل _regex التي تم التحقق من صحتها_؟

let a: RegExType_1
let b: RegExType_2

a = b // Is this allowed? Is RegExType_2 subtype of RegExType_1?
b = a // Is this allowed? Is RegExType_1 subtype of RegExType_2?

حيث RegExType_1 و RegExType_2 من أنواع السلاسل _regex التي تم التحقق من صحتها_.

تحرير: يبدو أن هذه المشكلة قابلة للحل في وقت كثير الحدود (راجع مشكلة التضمين للتعبيرات العادية ).

سيساعد أيضًا في TypeStyle: https://github.com/typestyle/typestyle/issues/5 : rose:

في JSX ، RyanCavanaugh أشخاصًا يضيفون سمات aria- (وربما data- ). أضاف شخص ما بالفعل توقيع فهرس سلسلة في DefinitelyTyped باعتباره ملفًا شاملاً. قد يكون توقيع فهرس جديد لهذا مفيدًا.

interface IntrinsicElements {
    // ....
    [attributeName: /aria-\w+/]: number | string | boolean;
}

اقتراح تصميم

هناك الكثير من الحالات التي يحتاج فيها المطورون إلى قيمة أكثر تحديدًا ثم مجرد سلسلة ، ولكن لا يمكنهم تعدادها على أنها اتحاد من سلسلة حرفية بسيطة ، مثل ألوان css ورسائل البريد الإلكتروني وأرقام الهواتف والرمز البريدي وملحقات swagger وما إلى ذلك. يستخدم لوصف مخطط كائن JSON له نقش وأنماط خصائص يمكن regex-validated string type و regex-validated string type of index .

الأهداف

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

نظرة عامة نحوية

يتكون تنفيذ هذه الميزة من 4 أجزاء:

نوع Regex تم التحقق من صحته

type CssColor = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
type Email = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i;
type Gmail = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@gmail\.com$/i;

نوع متغير تم التحقق منه من خلال Regex

let fontColor: /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;

ونفس الشيء ، ولكن أكثر قابلية للقراءة

let fontColor: CssColor;

نوع متغير من الفهرس تم التحقق منه من خلال Regex

interface UsersCollection {
    [email: /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i]: User;
}

ونفس الشيء ، ولكن أكثر قابلية للقراءة

interface UsersCollection {
    [email: Email]: User;
}

اكتب حارس لنوع متغير

setFontColorFromString(color: string) {
    fontColor = color;// compile time error
    if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(color)) {
        fontColor = color;// correct
    }
}

ونفس الشيء

setFontColorFromString(color: string) {
    fontColor = color;// compile time error
    if (!(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(color))) return;
    fontColor = color;// correct
}

واستخدام النوع المحدد لقراءة أفضل

setFontColorFromString(color: string) {
    fontColor = color;// compile time error
    if (CssColor.test(color)) {
        fontColor = color;// correct
    }
}

مثل

setFontColorFromString(color: string) {
    fontColor = color;// compile time error
    if (!(CssColor.test(color))) return;
    fontColor = color;// correct
}

اكتب gurard لنوع الفهرس

let collection: UsersCollection;
getUserByEmail(email: string) {
    collection[email];// type is any
    if (/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i.test(email)) {
        collection[email];// type is User
    }
}

مثل

let collection: UsersCollection;
getUserByEmail(email: string) {
    collection[email];// type is any
    if (!(/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i.test(email))) return;
    collection[email];// type is User
}

واستخدام النوع المحدد لقراءة أفضل

let collection: UsersCollection;
getUserByEmail(email: string) {
    collection[email];// type is any
    if (Email.test(email)) {
        collection[email];// type is User
    }
}

مثل

let collection: UsersCollection;
getUserByEmail(email: string) {
    collection[email];// type is any
    if (!(Email.test(email))) return;
    collection[email];// type is User
}

نظرة عامة دلالية

تعيينات

let email: Email;
let gmail: Gmail;
email = '[email protected]';// correct
email = '[email protected]';// correct
gmail = '[email protected]';// compile time error
gmail = '[email protected]';// correct
gmail = email;// obviously compile time error
email = gmail;// unfortunately compile time error too

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

// explicit cast
gmail = <Gmail>email;// correct
// type guard
if (Gmail.test(email)) {
    gmail = email;// correct
}
// another regex subtype declaration
type Gmail = Email & /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@gmail\.com$/i;
gmail = email;// correct

لسوء الحظ ، يجب أيضًا تقييد تعيين متغير string لمتغير regex-validated ، لأنه لا يوجد ضمان في وقت الترجمة بأنه سيتطابق مع regex.

let someEmail = '[email protected]';
let someGmail = '[email protected]';
email = someEmail;// compile time error
gmail = someGmail;// compile time error

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

let someEmail: '[email protected]' = '[email protected]';
let someGmail: '[email protected]' = '[email protected]';
email = someEmail;// correct
gmail = someGmail;// correct

اكتب تضييق الفهارس

للحالات البسيطة من الفهرس regex-validated type انظر النوع gurard لنوع الفهرس .
ولكن قد تكون هناك حالات أكثر تعقيدًا:

type Email = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i;
type Gmail = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@gmail\.com$/i;
interface UsersCollection {
    [email: Email]: User;
    [gmail: Gmail]: GmailUser;
}
let collection: UsersCollection;
let someEmail = '[email protected]';
let someGmail = '[email protected]';
collection['[email protected]'];// type is User
collection['[email protected]'];// type is User & GmailUser
collection[someEmail];// unfortunately type is any
collection[someGmail];// unfortunately type is any
// explicit cast is still an unsafe workaround
collection[<Email> someEmail];// type is User
collection[<Gmail> someGmail];// type is GmailUser
collection[<Email & Gmail> someGmail];// type is User & GmailUser

لا توجد مثل هذه المشكلة في الحروف:

let collection: UsersCollection;
let someEmail: '[email protected]' = '[email protected]';
let someGmail: '[email protected]' = '[email protected]';
collection[someEmail];// type is User
collection[someGmail];// type is User & GmailUser

ولكن بالنسبة للمتغيرات ، فإن الخيار الأفضل هو استخدام حراس الكتابة كما هو الحال في الأمثلة الأكثر واقعية التالية:

getUserByEmail(email: string) {
    collection[email];// type is any
    if (Email.test(email)) {
        collection[email];// type is User
        if (Gmail.test(email)) {
            collection[email];// type is User & GmailUser
        }
    }
    if (Gmail.test(email)) {
        collection[email];// type is GmailUser
    }
}

ولكن إذا استخدمنا تعريفًا أفضل للكتابة Gmail ، فسيكون لها نوع آخر من التضييق:

type Gmail = Email & /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@gmail\.com$/i;
getUserByEmail(email: string) {
    collection[email];// type is any
    if (Email.test(email)) {
        collection[email];// type is User
        if (Gmail.test(email)) {
            collection[email];// type is User & GmailUser
        }
    }
    if (Gmail.test(email)) {
        collection[email];// type is User & GmailUser
    }
}

النقابات والتقاطعات

في الواقع الأنواع الشائعة وأنواع regex-validated مختلفة حقًا ، لذلك نحن بحاجة إلى قواعد حول كيفية التعامل مع اتحاداتهم وتقاطعاتهم.

type Regex_1 = / ... /;
type Regex_2 = / ... /;
type NonRegex = { ... };
type test_1 = Regex_1 | Regex_2;// correct
type test_2 = Regex_1 & Regex_2;// correct
type test_3 = Regex_1 | NonRegex;// correct
type test_4 = Regex_1 & NonRegex;// compile time error
if (test_1.test(something)) {
    something;// type is test_1
    // something matches Regex_1 OR Regex_2
}
if (test_2.test(something)) {
    something;// type is test_2
    // something matches Regex_1 AND Regex_2
}
if (test_3.test(something)) {
    something;// type is Regex_1
} else {
    something;// type is NonRegex
}

علم الوراثة

لا توجد حالات خاصة للأدوية ، لذا يمكن استخدام النوع regex-validated مع الأدوية الجنيسة بنفس الطريقة التي تستخدم بها الأنواع المعتادة.
بالنسبة للأدوية ذات القيود مثل أدناه ، يتصرف النوع regex-validated مثل السلسلة:

class Something<T extends String> { ... }
let something = new Something<Email>();// correct

نظرة عامة على الإصدار

بخلاف الأنواع المعتادة ، يكون لـ regex-validated بعض التأثير على الانبعاث:

type Regex_1 = / ... /;
type Regex_2 = / ... /;
type NonRegex = { ... };
type test_1 = Regex_1 | Regex_2;
type test_2 = Regex_1 & Regex_2;
type test_3 = Regex_1 | NonRegex;
type test_4 = Regex_1 & NonRegex;
if (test_1.test(something)) {
    /* ... */
}
if (test_2.test(something)) {
    /* ... */
}
if (test_3.test(something)) {
    /* ... */
} else {
    /* ... */
}

سيتم تجميعها إلى:

var Regex_1 = / ... /;
var Regex_2 = / ... /;
if (Regex_1.test(something) || Regex_2.test(something)) {
    /* ... */
}
if (Regex_1.test(something) && Regex_2.test(something)) {
    /* ... */
}
if (Regex_1.test(something)) {
    /* ... */
} else {
    /* ... */
}

نظرة عامة على التوافق

لا توجد مشاكل مع هذه الميزة في التوافق ، نظرًا لوجود حالة فقط يمكن أن تكسرها وهي مرتبطة بهذا النوع regex-validated له تأثير على عكس النوع المعتاد ، لذلك هذا رمز TS صالح:

type someType = { ... };
var someType = { ... };

عندما لا يكون الرمز أدناه:

type someRegex = / ... /;
var someRegex = { ... };

لكن الثانية كانت غير صالحة بالفعل ، ولكن لسبب آخر (إعلان النوع كان خاطئًا).
لذا علينا الآن تقييد التصريح عن المتغير بالاسم نفسه للنوع ، في حال كان هذا النوع هو regex-validated .

ملاحظة

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

لقد نسيت أن أشير إلى بعض حالات التقاطعات والنقابات من أنواع regex-validated ، لكنني وصفتها في أحدث حالة اختبار. هل يجب أن أقوم بتحديث Design proposal لإظهار هذا التغيير الطفيف؟

Igmat ، سؤال حول اقتراح التصميم الخاص بك: هل يمكنك توضيح نظرة عامة على

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

let fontColor: /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
fontColor = "#000";

وهذا:

type CssColor: /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
let fontColor: CssColor;
fontColor = "#000";

كل شيء على ما يرام وليس هناك حاجة لإصدار التغييرات ، لأنه يمكن إيداع "#000" في وقت الترجمة.
ولكن علينا أيضًا التعامل مع التضييق من string إلى regex-validated أجل جعله مفيدًا. لذلك فكرت في هذا لكلا الإعدادين السابقين:

let someString: string;
if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(someString)) {
    fontColor = someString; // Ok
}
fontColor = someString; // compile time error

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

let someString: string;
let email: /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/I;
if (/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i.test(someString)) {
    email = someString; // Ok
}
email = someString; // compile time error

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

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

مرحبًا Igmat ، أعتقد أن هناك بعض الأشياء التي كان يجب أن أسأل عنها في البداية

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

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

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

igmat - ليس هناك شك في ذهني أن إصدار regexes في وقت التشغيل سيكون مفيدًا. ومع ذلك ، لا أعتقد أنه من الضروري أن تكون هذه الميزة مفيدة (ومن أصوات ما قاله DanielRosenwasser ، ربما لن تتم الموافقة عليها على أي حال). أنت قلت

ولكن علينا أيضًا التعامل مع التضييق من سلسلة إلى نوع تم التحقق من صحته لكي نجعله مفيدًا

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

function foo(bar: number) {
    let baz: /prefix:\d+/ = 'prefix:' + number;
}

لا يمكننا التأكد من تطابق الأنواع - ماذا لو كان الرقم سالبًا؟ وبما أن regexes تصبح أكثر تعقيدًا ، فإنها تصبح أكثر فوضوية وفوضى. إذا كنا نريد هذا حقًا ، فربما نسمح "بكتابة الاستيفاء: type Baz = /prefix:{number}/ ... لكن لا أعرف ما إذا كان الأمر يستحق الذهاب إلى هناك.

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

ضع في اعتبارك ما يلي:

type Color = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
let foo: Color = '#000000';
let bar: Color = '#0000'; // Error - string literal '#0000' is not assignable to type 'Color'; '#0000' does not match /^#([0-9a-f]{3}|[0-9a-f]{6})$/i
let baz: Color = '#' + config.userColorChoice; // Error - type 'string' is not assignable to type 'regex-validated-string'

هل تعتقد أن هذا بديل عملي؟

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

const emailRegex = /.../;
/**
 * assign it only with values tested to emailRegex 
 */
let email: string;
let userInput: string;
// somehow get user input
if (emailRegex.test(userInput)) {
    email = userInput;
} else {
    console.log('User provided invalid email. Showing validation error');
    // Some code for validation error
}

مع تنفيذ هذا الاقتراح ، سيبدو كما يلي:

type Email = /.../;
let email: Email;
let userInput: string;
// somehow get user input
if (Email.test(userInput)) {
    email = userInput;
} else {
    console.log('User provided invalid email. Showing validation error');
    // Some code for validation error
}

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

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

function foo(bar: number) {
    let baz: /prefix:\d+/ = 'prefix:' + number;
}

يجب أن يخطئ هذا الرمز بسبب اقتراحي. لكن هذا:

function foo(bar: number) {
    let baz: /prefix:\d+/ = ('prefix:' + number) as /prefix:\d+/;
}

أو هذا:

function foo(bar: number) {
    let baz: /prefix:\d+/;
    let possibleBaz: string = 'prefix:' + number;
    if (/prefix:\d+/.test(possibleBaz)) {
        baz = possibleBaz;
    }
}

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

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

export type Email = /.../;
export const Email = /.../;
let email: Email;
let userInput: string;
// somehow get user input
if (Email.test(userInput)) {
    email = <Email>userInput;
} else {
    console.log('User provided invalid email. Showing validation error');
    // Some code for validation error
}

أو للتقاطعات:

export type Email = /email-regex/;
export const Email = /email-regex/;
export type Gmail = Email & /gmail-regex/;
export const Gmail = {
    test: (input: string) => Email.test(input) && /gmail-regex/.test(input)
};
let gmail: Gmail;
let userInput: string;
// somehow get user input
if (Gmail.test(userInput)) {
    gmail = <Gmail>userInput;
} else {
    console.log('User provided invalid gmail. Showing validation error');
    // Some code for validation error
}

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

في الختام ، أود أن أقول أنه بعبارات بسيطة ، فإن النوع regex-validated هو متغير محدد النطاق ونوع مترجم.

DanielRosenwasser و alexanderbird حسنًا ، لدي فكرة أخرى عن ذلك. ماذا عن بناء الجملة مثل هذا:

const type Email = /email-regex/;

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

const Email = /email-regex/;

يبدو أن هذا أكبر من مجرد تحسين لهذا الاقتراح ، لأن هذا قد يسمح بشيء كهذا (المثال من مشروع مع Redux ):

export type SOME_ACTION = 'SOME_ACTION';
export const SOME_ACTION = 'SOME_ACTION' as SOME_ACTION;

يتم تحويلها إلى

export const type SOME_ACTION = 'SOME_ACTION';

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

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

let something: /some-regex-with-backreferences/ = `
long enough string to make regex.test significantly affect performance
`

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

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

DanielRosenwasser هل هناك أي أفكار؟ 😄 حول الاقتراح الأولي وعن الاقتراح الأخير. قد أحتاج إلى تقديم نظرة عامة أكثر تفصيلاً عن الثانية ، أليس كذلك؟

Igmat يحد اقتراح rylphs ؟ سيسمح هذا بالتحقق من صحة أكثر عمومية لجميع الأنواع الأولية:

type ColorLevel = (n:number) => n>0 && n<= 255
type RGB = {red:ColorLevel, green:ColorLevel, blue:ColorLevel};
let redColor:RGB = {red:255, green:0, blue:0}   //OK
let wrongColor:RGB = {red:255, green:900, blue:0} //wrong

ومع ذلك ، أظن أن توسيع هذه الآلية إلى ما بعد البدائية إلى الأنواع غير البدائية سيكون أكثر من اللازم.
نقطة واحدة ، المشكلة التي أثارها DanielRosenwasser - حول تطبيقات محرك regex المختلفة - سيتم تضخيمها: اعتمادًا على محرك Javascript الذي يعمل المترجم Typescript تحته ، قد تعمل وظيفة التحقق من الصحة بشكل مختلف.

zspitz يبدو واعدًا ولكن في رأيي يمكن أن يؤثر على أداء المترجم كثيرًا ، لأن الوظيفة ليست مقيدة بأي قواعد

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

لأن الوظيفة غير مقيدة بأية قواعد

هل لديك بعض الأمثلة المحددة في الاعتبار؟ ربما يكون من الممكن قصر صيغة التحقق على مجموعة فرعية "آمنة" / compile-time معروفة من Typescript.

ماذا عن جعل حراس النوع الذي يحدده المستخدم يحدد نوعًا جديدًا؟

// type guard that introduces new nominal type int
function isInt(value: number): value is type int { return /^\d+$/.test(value.toString()); }
// -------------------------------------^^^^ add type keyword here
function printNum(value: number) { console.log(value); }
function printInt(value: int) { console.log(value); }
const num = 123;
printNum(num); // ok
printInt(num); // error
if (isInt(num)) {
    printNum(num); // ok
    printInt(num); // ok
}

disjukr تبدو لطيفة ، ولكن ماذا عن تمديد النوع؟

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

سنحتاج إلى شيء مبدع جدًا للحصول على IMHO القابلية للتوسعة - لا يمكننا تحديد ما إذا كان حارس (وظيفة) من النوع التعسفي هو مجموعة فرعية من نوع تعسفي آخر.

يمكن أن نحصل على "القابلية للتمديد" بدائية باستخدام عقلية تأكيد النوع (لا أقول أن هذا شيء "إبداعي جدًا" - أقول إن هذه نقطة توقف حتى يأتي شخص ما بشيء مبدع جدًا):

function isInt(value: number): value is type int { return /^\d+$/.test(value.toString()); }
// assert that biggerInt extends int. No compiler or runtime check that it actually does extend.
function isBiggerInt(value: number): value is type biggerInt extends int { return /^\d{6,}$/.test(value.toString()); }
// -----------------------------------------------------------^^^^ type extension assertion
function printNum(value: number) { console.log(value); }
function printInt(value: int) { console.log(value); }
function printBiggerInt(value: biggerInt) {console.log(value); }

const num = 123;
printNum(num); // ok
printInt(num); // error
printBiggerInt(num); // error
if (isInt(num)) {
    printNum(num); // ok
    printInt(num); // ok
    printBiggerInt(num); // error
}
if (isBiggerInt(num)) {
    printNum(num); // ok
    printInt(num); // ok
    printBiggerInt(num); // ok
}

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

نوع من offtopic ، الرد على DanielRosenwasser التعليق الأول:
بالنسبة إلى القائمة المفصولة بفواصل ، يجب عليك استخدام نقاط الارتساء ^ و $ (إنها ذات صلة في معظم الحالات عندما تريد التحقق من صحة بعض السلاسل). وتساعد المراسي على تجنب التكرار ، على سبيل المثال ، سيكون التعبير العادي /^((dog|cat|fish)(,|$))+$/

السماح لأنواع السلاسل بأن تكون تعبيرات عادية /#[0-9]{6}/ والسماح بأنواع متداخلة في التعبيرات العادية ${TColor} :

type TColor = 'red' | 'blue' | /#[0-9]{6}/;
type TBorderValue = /[0-9]+px (solid|dashed) ${TColor}/

نتيجة:

let border1: TBorderValue = '1px solid red'; // OK
let border2: TBorderValue = '1px solid yellow'; // TSError: .....

حالة الاستخدام: توجد مكتبة مخصصة لكتابة أنماط CSS "type-safe" في TypeScript typestyle . ستساعد الوظيفة المقترحة أعلاه بشكل كبير ، نظرًا لأن المكتبة يجب أن تعرض طرقًا لاستخدامها في وقت التشغيل ، فإن أنواع Regex للسلسلة المقترحة ستتمكن بدلاً من ذلك من كتابة كود التحقق في وقت الترجمة وإعطاء المطورين تحسسًا رائعًا.

DanielRosenwasseralexanderbirdIgmat : IMO هذا الاقتراح سيغير قواعد اللعبة من أجل TypeScript وتطوير الويب. ما الذي يمنعها حاليًا من التنفيذ؟

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

لقد وصلت إلى هنا لأنني أتطلع إلى الحصول على نوع UUID وليس سلسلة ، وبالتالي فإن وجود regex يحدد السلسلة سيكون رائعًا في هذه الحالة + طريقة للتحقق من صحة النوع (مثال Email.test) سيكون مفيدًا أيضًا.

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

من المثير للاهتمام أن هذا يؤدي بشكل فعال إلى إنشاء نوع اسمي ، نظرًا لأننا لن نتمكن من إنشاء أي نوع فرعي / علاقات إحالة بين أي اثنين من regexps غير متطابق

RyanCavanaugh في وقت سابق maiermic علق

تحرير: يبدو أن هذه المشكلة قابلة للحل في وقت كثير الحدود (راجع مشكلة التضمين للتعبيرات العادية).

لكن هذا قد لا يكون جيدا بما فيه الكفاية؟ يأمل المرء بالتأكيد ألا يكون هناك الكثير من العلاقات العادية ، لكنك لا تعرف أبدًا.

فيما يتعلق بفحوصات النوع ، إذا لم نرغب في تكرار regexps ، و typeof a const ليست جيدة بما يكفي (مثل ملفات .d.ts) ، كيف يشعر TS تجاه valueof e ، الذي ينبعث القيمة الحرفية e iff e هي قيمة حرفية ، وإلا فهي خطأ (وتنبعث منها شيء مثل undefined

maxlk أيضًا خارج الموضوع ولكنني أخذت regex وقمت بتحسينه بحيث لا يتطابق مع الفواصل اللاحقة على الإدخال الصحيح: /^((dog|cat|fish)(,(?=\b)|$))+$/ مع الاختبار https://regex101.com/r/AuyP3g/1. يستخدم هذا المظهر الإيجابي لحرف كلمة بعد الفاصلة ، مما يجبر السابق على إعادة التحقق بطريقة جافة.

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

lgmat ماذا عن قصر بناء الجملة على وظائف السهم أحادي السطر ، باستخدام التعريفات المتاحة فقط في lib.d.ts ؟

هل هذه التحسينات الرائعة متاحة؟ ربما في إصدار ألفا على الأقل؟

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

+1. حالة الاستخدام الخاصة بنا شائعة جدًا ، فنحن بحاجة إلى تنسيق تاريخ سلسلة مثل "dd / mm / YYYY".

على الرغم من أنها ستكون ميزة رائعة للغاية كما هو مقترح ، إلا أنها تفتقر إلى الإمكانات:

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

أفضل طريقة هي الاستعانة بمصادر خارجية للتحليل والإرسال إلى مكون إضافي ، كما هو مقترح في # 21861 ، وبهذه الطريقة لا يمثل كل ما سبق مشكلة في سعر منحنى التعلم الأكثر حدة ، ولكن مهلا! يمكن تنفيذ فحص regexp فوق ذلك بحيث يظل الاقتراح الأصلي قائمًا ، حيث يتم الخروج بآليات أكثر تقدمًا

كما قلت ، ستكون الطريقة الأكثر عمومية هي موفري بناء الجملة المخصصين لأي حرفية: # 21861

أمثلة:

const uri: via URIParserAndEmitter = http://google.com; 
console.log(uri); // --> { protocol: 'http', host: 'google.com', path: undefined, query: undefined, hash: undefined }

const a: via PositiveNumberParser = 10; // --> 10
const b: via PositiveNumberParser = -10; // --> error

const date: via DateParser = 1/1/2019; // --> new Date(2019, 1, 1)


lgmat ماذا عن قصر بناء الجملة على وظائف السهم أحادي السطر ، باستخدام التعريفات المتاحة فقط في lib.d.ts ؟

zspitz التي من شأنها أن تجعل الكثير من الناس غير سعداء ، كما سيرون ، أن هذا ممكن ، لكن محظور عليهم ، أساسًا من أجل سلامتهم.

هل هذه التحسينات الرائعة متاحة؟ ربما في إصدار ألفا على الأقل؟

وبقدر ما أعلم أن هذا لا يزال بحاجة إلى اقتراح. gtamas ، AndrewEastwood

أعتقد أيضًا أن # 11152 سيؤثر على هذا.

Igmat يحد اقتراح rylphs ؟ سيسمح هذا بالتحقق من صحة أكثر عمومية لجميع الأنواع الأولية:

type ColorLevel = (n:number) => n>0 && n<= 255
type RGB = {red:ColorLevel, green:ColorLevel, blue:ColorLevel};
let redColor:RGB = {red:255, green:0, blue:0}   //OK
let wrongColor:RGB = {red:255, green:900, blue:0} //wrong

ومع ذلك ، أظن أن توسيع هذه الآلية إلى ما بعد البدائية إلى الأنواع غير البدائية سيكون أكثر من اللازم.

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

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

نقطة واحدة ، المشكلة التي أثارها DanielRosenwasser - حول تطبيقات محرك regex المختلفة - سيتم تضخيمها: اعتمادًا على محرك Javascript الذي يعمل المترجم Typescript تحته ، قد تعمل وظيفة التحقق من الصحة بشكل مختلف.

هذا صحيح ، هذا أمر سيء ، ولكن يمكننا حله من خلال تحديد الجزء "الحديث" من regExp الذي نحتاجه لقاعدة التعليمات البرمجية الخاصة بنا. سيكون افتراضيًا إلى الوضع العادي (هل هو ES3؟) regexp ، الذي يعمل في كل عقدة . وخيار لتمكين الأعلام الجديدة والتأكيدات الخلفية.

const unicodeMatcher = /\u{1d306}/u;
let value: typeof unicodeMatcher;
function(input: string) {
  value = input;  // Invalid
  if (input.match(unicodeMatcher)) {
    value = input;  // OK
  }
}

إذا قام المستخدم بتعطيل العلامة ذات العلامات المتقدمة.

let value: typeof unicodeMatcher = '𝌆';  // Warning, string literal isn't checked, because `variable` is of type `/\u{1d306}/u`.

لن يقوم TypeScript بتقييم RegExp المتقدم ، إذا لم يتم إخباره بذلك. لكنني أقترح أن يكون هذا بمثابة تحذير ، وشرح ما يحدث وكيفية تمكين فحص RegExp المتقدم.

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

let value: typeof unicodeMatcher = '𝌆';  // OK

إذا قام المستخدم بتمكين العلم بعلامات متقدمة ودعمته العقدة الخاصة به.

let value: typeof unicodeMatcher = '𝌆';  
// Error, NodeJS does not support advanced RegExp, upgrade NodeJS to version X.Y.Z, or disable advanced RegExp checking.

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

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

AFAIK كان الاقتراح الأصلي جيدًا بصرف النظر عن نظرة عامة على Emit وهو أمر

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

type letterExpression = /[a-zA-Z]/;
let exp: letterExpression;
exp = /[a-zA-Z]/; // works
exp = /[A-Za-z]/; // error, the expressions do not match

type letter = patternof letterExpression;
type letter = patternof /[a-zA-Z]/; // this is equivalent

let a: letter;
a = 'f'; // works
a = '0'; // error
const email = /some-long-email-regex/;
type email = patternof typeof email;

declare let str: string;
if (str.match(email)) {
  str // typeof str === email
} else {
  str // typeof str === string
}

@ m93a لم أفكر في مثل هذا الحل مع عامل نوع إضافي ، عندما كنت أعمل على الاقتراح الأولي.

يعجبني هذا النهج لإزالة تأثير الانبعاث الناجم عن الأنواع ، على الرغم من أن هذا يبدو أكثر تفصيلاً.

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

سوف يستغرق الأمر 4 خطوات:

  1. إضافة نوع regexp-validated string literal :
    TypeScript type Email = /some-long-email-regex/;
  2. دعنا نغير واجهة RegExp في core lib إلى عامة:
    TypeScript interface RegExp<T extends string = string> { test(stringToTest: string): stringToTest is T; }
  3. تغيير نوع الاستنتاج للتعبير الحرفي في الكود الفعلي:
    TypeScript const Email = /some-long-email-regex/; // infers to `RegExp</some-long-email-regex/>`
  4. إضافة نوع المساعد باستخدام ميزة conditional types ، مثل InstanceType :
    TypeScript type ValidatedStringType<T extends RegExp> = T extends RegExp<infer V> ? V : string;

مثال على الاستخدام:

const Email = /some-long-email-regex/;
type Email = ValidatedStringType<typeof Email>;

const email: Email = `[email protected]`; // correct
const email2: Email = `emexample.com`; // compile time error

let userInput: string;
if (Email.test(userInput)) {
    // `userInput` here IS of type `Email`
} else {
    // and here it is just `string`
}

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

let a: 'foo' = 'foo'; // works
let b: 42 = 42; // works
let c: /x/ = /x/; // error

لكني أعتقد أن بساطة اقتراحك تفوق العيب الوحيد.

تحرير: لا أحب طول ValidatedStringType<R> . إذا قررنا استدعاء أنماط السلاسل التي تم التحقق من صحتها ، فيمكننا استخدام PatternOf<R> بعد كل شيء. أنا لا أقول أن كتابتك تستغرق وقتًا أطول للكتابة ، فمعظم الناس يكتبون الأحرف الثلاثة الأولى ويضغطون على علامة التبويب. إنه فقط له تأثير أكبر على انتشار الكود.

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

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

RyanCavanaugh هل يمكنك أن تخبرنا برأيك في هذا؟ (على وجه التحديد الاقتراح الأصلي والتعليقات الأربعة الأخيرة (باستثناء هذا التعليق).) شكرا لك! : +1:

ماذا عن وجود وسيطة عامة للسلسلة ، تكون افتراضية .* ؟

let a: 'foo' = 'foo'; // works
let b: 42 = 42; // works
let c: /x/ = /x/; // works
let d: string<x.> = 'xa'; // works

يمكن اعتبار السلسلة الحرفية 'foo' سكرًا مقابل string<foo>

لا أحب حقًا طول ValidatedStringType<R> . إذا قررنا استدعاء السلاسل التي تم التحقق من صحتها _ أنماط_ ، فيمكننا استخدام PatternOf<R> بعد كل شيء.

@ m93a ، IMO ، في هذه الحالة سيكون من الأفضل الاتصال بهم PatternType<R> ليتوافق مع المساعدين الحاليين InstanceType و ReturnType .

@ amir-arad ، ممتع. كيف سيبدو interface RegExp في هذه الحالة؟

RyanCavanaugh يمكنني إعادة كتابة الاقتراح الأصلي بالطريقة التي تم العثور عليها حديثًا إذا كان ذلك سيساعد. هل علي أن؟

@ amir-arad يتعارض بناء الجملة المقترح مع باقي نصوص TypeScript. الآن يمكنك فقط تمرير الأنواع كوسيطة عامة ، وليس تعبيرًا عشوائيًا. قد يكون بناء الجملة الذي اقترحته محيرًا للغاية.

فكر في الأنواع العامة مثل الوظائف التي تأخذ نوعًا وتعيد نوعًا. إن الجزأين التاليين من التعليمات البرمجية قريبان جدًا من حيث المعنى وبناء الجملة:

function foo(str: string) {
  return str === 'bar' ? true : false
}

type foo<T extends string> = T extends 'bar' ? true : false;

بناء الجملة الجديد يشبه اقتراح كتابة التعبير العادي في JavaScript let all = String(.*) والذي سيكون بمثابة إساءة استخدام قبيحة لبناء جملة استدعاء الوظيفة. لذلك لا أعتقد أن اقتراحك منطقي.

@ m93a كان اقتراحي نوع التعليقات التوضيحية وليس جافا سكريبت.

Igmat من اعلى رأسي ماذا عن:

interface RegExp {
    test(stringToTest: string): stringToTest is string<this>;
}

@ amir-arad ، آسف ، لا يمكنني إضافة المزيد من التفاصيل القيمة إلى اقتراحك ، لكن للوهلة الأولى يبدو أنه تغيير مهم للغاية لمجمع TS بالكامل ، لأن string بدائي للغاية.

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

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

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

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

تحرير: إصلاح الخطأ.

تسقط لتلاحظ أن Mithril يمكنه حقًا استخدام هذه ، وأن كونك آمنًا في الحالة العامة يكاد يكون مستحيلًا بدونها. هذا هو الحال مع كل من صيغة Hyperscript و JSX. (نحن ندعم كليهما.)

  • خطافات دورة الحياة الخاصة بنا ، oninit ، oncreate ، onbeforeupdate ، onupdate ، onbeforeremove ، و onremove ، لها خطافات خاصة بها نماذج خاصة.
  • معالجات الأحداث على DOM vnodes هي حرفياً أي شيء آخر يبدأ بـ on ، ونحن ندعم كلاً من وظائف مستمع الأحداث وكائنات مستمع الأحداث (مع طرق handleEvent ) ، المحاذاة مع addEventListener و removeEventListener .
  • نحن ندعم المفاتيح والمراجع حسب الاقتضاء.
  • يتم التعامل مع كل شيء آخر كسمة أو خاصية ، اعتمادًا على وجودها على عقدة DOM نفسها.

لذلك باستخدام نوع سلسلة تم التحقق من صحتها من regex + نوع نفي ، يمكننا القيام بما يلي لـ DOM vnodes:

interface BaseAttributes {
    // Lifecycle attributes
    oninit(vnode: Vnode<this, Vnode<Attributes, []>>): void;
    oncreate(vnode: Vnode<this, Vnode<Attributes, []>>): void;
    onbeforeupdate(
        vnode: Vnode<this, Vnode<Attributes, []>>,
        old: Vnode<this, Vnode<Attributes, []>>
    ): void;
    onupdate(vnode: Vnode<this, Vnode<Attributes, []>>): void;
    onbeforeremove(vnode: Vnode<this, Vnode<Attributes, []>>): void | Promise<void>;
    onremove(vnode: Vnode<this, Vnode<Attributes, []>>): void;

    // Control attributes
    key: PropertyKey;
}

interface DOMAttributes extends BaseAttributes {
    // Event handlers
    [key: /^on/ & not keyof BaseAttributes]: (
        ((this: Element, ev: Event) => void | boolean) |
        {handleEvent(ev: Event): void}
    );

    // Other attributes
    [key: keyof HTMLElement & not keyof BaseAttributes & not /^on/]: any;
    [key: string & not keyof BaseAttributes & not /^on/]: string;
}

interface ComponentAttributes extends BaseAttributes {
    // Nothing else interesting unless components define them.
}

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

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

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

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

  • `value` - هذا يعادل حرفيا "value"
  • `value${"a" | "b"}` - هذا يعادل حرفيا "valuea" | "valueb"
  • `value${string}` - هذا مكافئ وظيفيًا لـ regexp /^value/ ، لكن "value" و "valuea" و "valueakjsfbf aflksfief fskdf d" كلها قابلة للتخصيص لها.
  • `foo${string}bar` - هذا يعادل وظيفيًا التعبير العادي /^foo.*bar$/ ، لكن تطبيعه أسهل قليلاً.
  • يمكن أن يكون هناك ، بالطبع ، استيفاءات متعددة. `foo${string}bar${string}baz` هو نوع حرفي صالح للقالب.
  • يجب أن تمتد عمليات الإقحام string ، ويجب ألا تكون متكررة. (الشرط الثاني لأسباب فنية).
  • يمكن تعيين النوع الحرفي للقالب A إلى نوع حرفي للقالب B إذا وفقط إذا كانت مجموعة السلاسل القابلة للتخصيص إلى A مجموعة فرعية من مجموعة السلاسل القابلة للتخصيص إلى B .

بالإضافة إلى ما سبق ، قد يوجد نوع خاص starof T ، حيث يجب أن يتكون T من أنواع حرفية لسلسلة مكونة من حرف واحد فقط. string سيكون موجودًا كاسم مستعار من النوع starof (...) ، حيث ... هو اتحاد كل القيم الحرفية لسلسلة أحرف UCS-2 الفردية من U + 0000 إلى U + FFFF ، بما في ذلك وحيد بدائل. يتيح لك هذا تحديد القواعد الكاملة للحروف الرقمية ES base-10 ، على سبيل المثال:

type DecimalDigit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
type Decimal = `${DecimalDigit}{starof DecimalDigit}`

type Numeric = `${(
    | Decimal
    | `${Decimal}.${starof DecimalDigit}`
    | `.${Decimal}`
)}${"" | (
    | `E${Decimal}` | `E+${Decimal}` | `E-${Decimal}`
    | `e${Decimal}` | `e+${Decimal}` | `e-${Decimal}`
)}`

وبالمثل ، يمكن تعديل بعض الطرق المضمنة لإرجاع مثل هذه الأنواع:

  • Number.prototype.toString(base?) - يمكن أن يؤدي هذا إلى إرجاع النوع Numeric أعلاه أو بعض المتغيرات منه لـ base s المعروفة بشكل ثابت.
  • +x ، x | 0 ، parseInt(x) ، وما شابه - عندما يُعرف أن x هو Numeric كما هو محدد أعلاه ، يمكن للنوع الناتج يتم الاستدلال عليها بشكل مناسب كنوع رقم حرفي.

وأخيرًا ، يمكنك استخراج المجموعات المتطابقة مثل: Key extends `on${infer EventName}` ? EventTypeMap[TagName][EventName] : never . يفترض استخراج القالب أنه يعمل دائمًا مع الأسماء الكاملة ، لذلك عليك أن تستخدم صراحة ${string} الاستيفاء للبحث عن التضمين النسبي. هذا ليس جشعًا ، لذا فإن ` "foo.bar.baz" extends $ {infer T}. $ {infer U} ? [T, U] : never إرجاع ["foo", "bar.baz"] ، وليس ["foo.bar", "baz"] .


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

  • لغة فارغة: ""
  • الاتحاد: "a" | "b"
  • التسلسل: `${a}${b}`
  • نجم كليني (جزئيًا): starof T ( T يمكن أن يحتوي فقط على أحرف فردية واتحادات.)

قد يؤدي هذا مشكلة تماثل الرسم البياني الفرعي

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

  2. يمكنك نمذجة النوع الموحد بأكمله كرسم بياني موجه ، حيث:

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

    وفقًا لمحادثة Math.SE هذه لفترة وجيزة (بدأت هنا تقريبًا) ، وجدت أن هذا الرسم البياني الناتج سيكون له جنس محدد (أي مع عدد محدود من القفزات فوق الحواف الأخرى *) وغياب أي starof أنواع المساواة في النوع تقلل من ذلك إلى مشكلة متعددة الحدود وبافتراض أنك تطبيع النقابات ، فهي أيضًا ليست بطيئة للغاية لأنها أسرع إلى حد ما من تكافؤ الشجرة. أظن بشدة أن الحالة العامة لهذا الاقتراح بأكمله (مجموعة فرعية من مشكلة تماثل الرسم البياني الفرعي) هي أيضًا وقت متعدد الحدود مع معاملات معقولة. (تحتوي مقالة Wikipedia المرتبطة أعلاه على بعض الأمثلة في "الخوارزميات" وأقسام المراجع حيث قد يتم تطبيق أغلفة خاصة.)


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


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


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

* من الناحية الحسابية ، يتم تعريف الجنس بشكل غير متوقع قليلاً بالنسبة لنا نحن المبرمجين (الحد الأدنى لعدد الثقوب التي تحتاج إلى حفرها في سطح لرسم الرسم البياني دون أي قفزات) ، ولكن الجنس المحدود (عدد محدود من الثقوب) يعني عددًا محدودًا من القفزات .

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

// This would work as a *full* type implementation mod implementations of `HTMLTypeMap` +
// `HTMLEventMap`
type BaseAttributes = {
    // Lifecycle attributes
    oninit(vnode: Vnode<this, Vnode<Attributes, []>>): void;
    oncreate(vnode: Vnode<this, Vnode<Attributes, []>>): void;
    onbeforeupdate(
        vnode: Vnode<this, Vnode<Attributes, []>>,
        old: Vnode<this, Vnode<Attributes, []>>
    ): void;
    onupdate(vnode: Vnode<this, Vnode<Attributes, []>>): void;
    onbeforeremove(vnode: Vnode<this, Vnode<Attributes, []>>): void | Promise<void>;
    onremove(vnode: Vnode<this, Vnode<Attributes, []>>): void;

    // Control attributes
    key: PropertyKey;
}

interface HTMLTypeMap {
    // ...
}

interface HTMLEventMap {
    // ...
}

// Just asserting a simple constraint
type _Assert<T extends true> = never;
type _Test0 = _Assert<
    keyof HTMLTypeMap[keyof HTMLTypeMap] extends `on${string}` ? false : true
>;

type EventHandler<Event> =
    ((this: Element, ev: Event) => void | boolean) |
    {handleEvent(ev: Event): void};

type Optional<T> = {[P in keyof T]?: T[P] | null | undefined | void}

type DOMAttributes<T extends keyof HTMLAttributeMap> = Optional<(
    & BaseAttributes
    & {[K in `on${keyof HTMLEventMap[T]}` & not keyof BaseAttributes]: EventHandler<(
        K extends `on${infer E}` ? HTMLEventMap[E] : never
    )>}
    & Record<
        keyof `on${string & not keyof HTMLEventMap}` & not keyof BaseAttributes,
        EventHandler<Event>
    >
    & Pick<HTMLTypeMap[T], (
        & keyof HTMLTypeMap[T]
        & not `on${string}`
        & not keyof BaseAttributes
    )>
    & Record<(
        & string
        & not keyof HTMLTypeMap[T]
        & not keyof BaseAttributes
        & not `on${string}`
    ), string | boolean>
)>;

تحرير: سيؤدي هذا أيضًا إلى تمكين كتابة 90٪ بشكل صحيح من طريقة Lodash _.get والطرق ذات الصلة باستخدام اختصار الخاصية ، مثل طريقة _.property(path) واختصارها _.map(coll, path) . من المحتمل أن يكون هناك العديد من الأشياء الأخرى التي لا أفكر فيها أيضًا ، ولكن ربما يكون هذا هو أكبر ما يمكنني التفكير فيه. (سأترك تنفيذ هذا النوع كتمرين للقارئ ، لكن يمكنني أن أؤكد لك أنه ممكن مع مزيج من ذلك والخدعة المعتادة للأنواع الشرطية مع سجل مفهرس فورًا ، شيء مثل {0: ..., 1: ...}[Path extends "" ? 0 : 1] ، لمعالجة سلسلة المسار الثابت.)

توصيتي هي أن نركز جهودنا على تنفيذ موفري الأنواع ، والتي يمكن استخدامها لتنفيذ أنواع regex.

لماذا اكتب الموفرين بدلاً من التنفيذ المباشر لأنواع regex؟ لأن

  1. إنه حل أكثر عمومية ويضيف العديد من الاحتمالات الجديدة إلى TypeScript مما يسهل الحصول على الدعم من مجموعة أكبر من المطورين بخلاف أولئك الذين يرون القيمة في أنواع سلاسل regex.
  2. يبدو أن مالكي الريبو المطبوع على الورق منفتحون على هذه الفكرة ، وينتظرون الاقتراح الصحيح. انظر # 3136

يحتوي F # على موفر نوع regex مفتوح المصدر.

بعض المعلومات عن موفري النوع: https://link.medium.com/0wS7vgaDQV

يمكن للمرء أن يتخيل أنه بمجرد تنفيذ موفري النوع وتنفيذ موفر نوع regex كمكتبة مفتوحة المصدر ، يمكن للمرء استخدامه على النحو التالي:

type PhoneNumber = RegexProvider</^\d{3}-\d{3}-\d{4}$/>
const acceptableNumber: PhoneNumber = "123-456-7890"; //  no compiler error
const unacceptableNumber: PhoneNumber = "hello world"; // compiler error

AlexLeung لست مقتنعًا أن هذه هي الطريقة الصحيحة للذهاب ، على الأقل ليس لهذا الطلب.

  • TypeScript مكتوب هيكليًا ، وليس مكتوبًا اسميًا ، وللتلاعب الحرفي بالسلسلة ، أريد الاحتفاظ بهذه الروح الهيكلية. موفري النوع من هذا القبيل سينشئون نوعًا فرعيًا اسميًا string حيث لا يتم التعامل مع RegexProvider</^foo$/> على أنه مكافئ لـ "foo" ، ولكن نوع فرعي اسمي منه. علاوة على ذلك ، سيتم التعامل مع RegexProvider</^foo$/> و RegexProvider</^fo{2}$/> على أنهما نوعان مختلفان ، وهذا شيء لست من المعجبين به. بدلاً من ذلك ، يتكامل اقتراحي بشكل مباشر مع السلاسل الموجودة في جوهرها ، ويتم إبلاغه بشكل مباشر بنظرية التعرف على اللغة الرسمية لضمان ملاءمتها بشكل طبيعي.
  • باستخدام خاصتي ، لا يمكنك فقط ربط السلاسل ، ولكن يمكنك استخراج أجزاء من السلاسل عبر Key extends `on${infer K}` ? K : never أو حتى Key extends `${Prefix}${infer Rest}` ? Rest : never . مقدمي نوع لا تقدم هذه الوظيفة، وليس هناك طريقة واضحة كيف ينبغي إذا كان من المقرر أن تضاف هذه الوظيفة.
  • المنجم أبسط إلى حد كبير على المستوى المفاهيمي: أقترح فقط أن نضيف أنواع تسلسل السلسلة ، وبالنسبة إلى RHS للأنواع الشرطية ، القدرة على استخراج معكوسها. أقترح أيضًا أن تتكامل مع string نفسها لتحل محل regexp /.*/ . لا يتطلب أي تغييرات في واجهة برمجة التطبيقات ، وبغض النظر عن الجزأين المعقدين نظريًا اللذين يتم فصلهما في الغالب عن بقية قاعدة الكود ، فإن حساب ما إذا كان نوع القالب الحرفي قابلاً للتخصيص إلى آخر واستخراج شريحة من سلسلة ، مشابه ، إن لم يكن أبسط ، لتنفيذ.

راجع للشغل ، لا يزال بإمكان اقتراحي كتابة هذا المثال PhoneNumber أيضًا. إنها مطولة أكثر قليلاً ، لكنني أحاول تصميم البيانات الموجودة بالفعل في أرض TS ، وليست البيانات الموجودة في مكان آخر (ما هو أكثر موفري أنواع F # إفادة). (تجدر الإشارة إلى أن هذا سيتوسع تقنيًا إلى القائمة الكاملة لأرقام الهواتف المحتملة هنا.)

type D = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
type PhoneNumber = `${D}${D}${D}-${D}${D}${D}-${D}${D}${D}${D}`;

سيتم التعامل مع RegexProvider ^ foo $ /> و RegexProvider ^ fo {2} $ /> كنوعين متميزين

قد يطلب موفرو النوع تنفيذ بعض الطرق equals أو compare ، حتى يتمكن مؤلف موفر النوع لموفر نوع regex من تحديد أن كلتا الحالتين المذكورتين أعلاه هما نوعان متكافئان. يمكن لمؤلف مزود النوع تنفيذ الكتابة الهيكلية أو الاسمية كما يحلو لهم.

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

type D = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
type PhoneNumber = StringTemplateMatcherProvider<D, D, D, "-", D, D, D, "-", D, D, D, D>;

AlexLeung ولكن هل يمكن "123-456-7890" النوع كثيرًا ).

شبه متعلق بالمناقشة المطروحة ، ماذا لو لم يكن النوع بطول ثابت (مثل رقم الهاتف)؟ أحد المواقف التي كنت أرغب في استخدامها مؤخرًا هو تخزين اسم غرفة بالتنسيق thread_{number} .

التعبير المعتاد الذي يطابق هذه القيمة هو thread_[1-9]\d* . مع ما يتم اقتراحه ، لا يبدو أنه من الممكن (أو حتى ممكن) مطابقة مثل هذا التنسيق. يمكن أن يكون الجزء العددي للقيمة _ أي طول_ أكبر من الصفر في هذه الحالة.

jhpratt قمت بمراجعة اقتراحي لاستيعاب ذلك ، في شكل starof ("0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9")/^\d*$/ ، لأنه لم يتطلب سوى تغيير بسيط فيه. إنه يحسن بنفس الطريقة التي يعمل بها string مثل /^[\u0000-\uFFFF]*$/ ، لذلك قررت المضي قدمًا وتعميم ذلك.

لا أريد تمديد starof أكثر من ذلك ، مثل قبول اتحادات تعسفية غير متكررة ، بسبب مخاوف تتعلق بالتعقيد الحسابي: يمكن التحقق مما إذا كان هناك تعبيرين عاديين تعسفيين * متكافئين في مساحة متعددة الحدود أو جدًا في الممارسة ولا يمكنك الحصول على AFAICT في كلا الاتجاهين. أضف دعمًا للتربيع (مثل a{2} ) ، وهو في الأساس غير عملي (التعقيد الأسي) . هذا فقط من أجل التكافؤ ، والتحقق مما إذا كان regexp يطابق مجموعة فرعية من السلاسل يتطابق مع regexp آخر ، وهو مطلوب للتحقق من قابلية التخصيص ، من الواضح أنه سيكون أكثر تعقيدًا.

* التعبيرات العادية بالمعنى الحسابي: أقوم فقط بتضمين الأحرف الفردية ، () ، (ab) ، (a|b) ، و (a*) ، حيث a و b (من المحتمل أن يكونا مختلفين) لكل عضو في هذه القائمة.

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

على سبيل المثال ، لنفترض أننا نستخدم ":" للإشارة إلى أن العنصر التالي هو أداة تحقق (استبدل ما تريد بـ ":" إذا كان لديك رأي في هذا):

type email = string : (s) => { return !!s.match(...) }
type phone_number = string : (n) => { return !!String(n).match(...) }
type excel_worksheet_name = string : (s) => { return (s != "History") && s.length <= 31 && ... }

كبداية أولية ، يمكن أن تقبل الكتابة المطبوعة فقط وظائف التحقق من الصحة التي:

  • وسيط واحد ، وهو مطلوب / يفترض أن يكون من النوع "الأساسي"
  • فقط الإشارة إلى المتغيرات التي تم تحديدها في وظيفة المدقق
  • إرجاع قيمة (والتي سيتم إجبارها على منطقي في عملية التحقق من الصحة)

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

بالإضافة إلى ذلك ، إذا لزم الأمر لتقييد النطاق الأولي بحجم يمكن التحكم فيه:

  • لا يمكن إضافة وظائف التحقق إلا إلى مجموعة فرعية من الأنواع الأصلية (سلسلة ، رقم)

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

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

بالإضافة إلى أنك لا تريد عمومًا أن يقوم المترجم بتشغيل أي شيء ترميه عليه ، خاصة الوظائف المحسّنة بشكل سيئ أو الخبيثة تمامًا while(true){} . إذا كنت تريد البرمجة الوصفية ، فعليك تصميمها بذكاء. مجرد السماح بتشغيل كود وقت التشغيل بشكل عشوائي في وقت الترجمة سيكون "طريقة PHP" للقيام بذلك.

أخيرًا ، الصيغة التي تقترحها تغير النمط المعتاد

let runtime: types = runtime;

(أي الأنواع بعد القولون) من الداخل إلى الخارج ، على نحو فعال

type types = types: runtime;

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

لا يمكن لهذه الوظائف

بالطبع يمكنهم، إذا كان المترجم له ECMAScript وقت التشغيل المتاحة لها ( tsc يفعل، راجع للشغل!). من الواضح أن لديك مشكلة غموض مع دلالات وقت الترجمة مثل fetch() مقابل دلالات وقت التشغيل ، ولكن هذا ما يدور حوله التكرار.

مجرد السماح بتشغيل كود وقت التشغيل بشكل عشوائي في وقت الترجمة سيكون "طريقة PHP" للقيام بذلك.

إنها تشبه إلى حد كبير وظائف C ++ constexpr ، وهي جيدة. الحل هناك هو أن نقول أن constexpr يمكن فقط استخدام constexpr ، لكن كل شيء يمكن أن يستخدم constexpr . ثم يمكن أن يكون لديك إصدارات متكافئة من نظام الملفات constexpr لنظام ملفات وقت الترجمة والتي يمكن أن تكون قوية للغاية.

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

لذا أشكرك على اقتراحك ، لكنها بالتأكيد فكرة سيئة.

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

يبدو أن المناقشة حول هذه الميزة قد توقفت في الوقت الحالي ( العلاقات العامة مغلقة ووفقًا لفريق Design Notes _ لا ترغب في الالتزام بذلك حتى يكون لدينا أنواع اسمية وتوقيعات فهرس معممة ، ويجب أن نعرف كيف تبدو هذه ._).

على أي حال ، أريد أن أقترح امتدادًا افتراضيًا آخر للعلاقات العامة الحالية من شأنه أن يدعم استخراج نمط regex (قدم isiahmeadows اقتراحه الخاص ، لكن

أنا حقًا أحب العلاقات العامة الحالية infer ). ببساطة لأن الناس لديهم بالفعل حدس أنه في الوظيفة العامة يمكنك "استخراج" أنواع من الكائنات الحرفية التي تم تمريرها.

على سبيل المثال لدينا هذا النوع.

type Prop1 = /(\w)\.(\w)/

ويمكننا استخدام هذا النوع لاختبار الأنواع الحرفية

const goodLiteral = "foo.bar";
const badLiteral = "foo";
const regextTest: Prop1 = goodLiteral; //no error
const regextTest: Prop1 = badLiteral; //compiler error

function funProp1(prop: Prop1) { } 

funProp1(goodLiteral); //no error
funProp1(badLiteral); //error

ومع ذلك ، عندما نستخدم نوع Regex في معلمة الوظيفة ، يمكننا استخدام صيغة الأقواس الزاوية ليعني أننا نريد استنتاج سلاسل متطابقة. على سبيل المثال

type Prop1 = /(\w)\.(\w)/
const Prop1 = /(\w)\.(\w)/

const goodLiteral = "foo.bar";
const badLiteral = "foo";

function funProp1<M1 extends string, M2 extends string>(prop: Prop1<M1, M2>) : [M1, M2] 
{
    const m = prop.match(Prop1);
    return [m[1], m[2]];
} 

const res1 = funProp1(goodLiteral); //no error. Function signature inferred to be funProp<"foo", "bar">(prop: Prop1<"foo", "bar">) : ["foo", "bar"]
const res2 = funProp1(badLiteral); //compiler error

لاحظ أن النوع المستنتج من res1 هو ["foo", "bar"]

هل هي مفيدة؟

  1. Ember.js / Lodash الحصول على وظيفة

يمكنك تنفيذ برنامج getter لـ "مسار السلسلة" من النوع الآمن حتى يعمل هذا الرمز:

const deep = get(objNested, "nested.very.deep")

ولكن ربما يتطلب الأمر حل هذه المشكلة إذا أردنا تجنب العديد من الأحمال الزائدة للحصول على أقصى عدد ثابت من "عمق" get .

  1. استخدم المعلمات المستخرجة في الأنواع المعينة.

على سبيل المثال ، إذا تمكنا من القيام بشيء مثل هذا https://github.com/Microsoft/TypeScript/issues/12754. ثم يمكن أن يكون لدينا إمكانية عكس الوظيفة (إزالة بعض البادئة / اللاحقة من جميع خصائص النوع المحدد). ربما يحتاج هذا الشخص إلى تقديم شكل أكثر عمومية من بناء الجملة المكتوب المعين لاختيار مفتاح جديد للخاصية (على سبيل المثال ، بناء الجملة مثل { [ StripAsyncSuffix<P> for P in K ] : T[P] } ، اقترح شخص ما بالفعل شيئًا من هذا القبيل)

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

هذا شيء يمكننا القيام به.

أقوم حاليًا ببناء قواعد Lint مخصصة حتى أتمكن من التحقق من صحة عناوين url - على الرغم من ذلك ، سيكون هذا أسهل بكثير إذا تمكنا من تحديد المعلمات الاختيارية - والتي تتطلب regex حتى نتمكن من التحقق من صحة معرفاتنا

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

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

الحل البديل لهذا حاليًا هو استخدام واجهة علامة مثل هذه .

interface TickerSymbol extends String {}

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

interface TickerSymbol extends String {}
var symbol: TickerSymbol = 'MSFT';
// declare var tickers: {[symbol: TickerSymbol]: any}; // Error: index key must be string or number
declare var tickers: {[symbol: string]: any};
// tickers[symbol]; // Type 'TickerSymbol' cannot be used as an index type
tickers[symbol as string]; // OK

ومع ذلك ، يبدو أن JavaScript مناسب لنوع الفهرس String (بحرف S كبير).

var obj = { one: 1 }
var key = new String('one');
obj[key]; // TypeScript Error: Type 'String' cannot be used as an index type.
// but JS gives expected output:
// 1

DanielRosenwasser لدي اقتراح هنا ، وتم إنشاء اقتراح منفصل في أواخر عام 2016 ، فهل يمكن تحديث ملصقات هذا؟

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

الجوانب الإشكالية للاقتراحات حتى الآن

إنشاء أنواع الإنبعاث

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

https://github.com/microsoft/TypeScript/issues/6579#issuecomment -220180091 - ينشئ وظيفة ونوعًا في نفس الوقت

type Integer(n:number) => String(n).macth(/^[0-9]+$/)

https://github.com/microsoft/TypeScript/issues/6579#issuecomment -261519733 - يقوم بهذا أيضًا

type CssColor = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
// ... later
setFontColorFromString(color: string) {
    fontColor = color;// compile time error
    if (CssColor.test(color)) {
    //  ^^^^^^^^ no value declaration of 'CssColor' !
        fontColor = color;// correct
    }
}

سأكرر: هذا ليس بداية . أنواع في نسخة مطبوعة على الآلة الكاتبة هي composable وينفث JS من أنواع غير ممكن في هذا العالم. يحتوي الاقتراح الأطول حتى الآن على انبعاث واسع النطاق من الأنواع ؛ هذا غير عملي. على سبيل المثال ، قد يتطلب ذلك انبعاثًا واسعًا موجهًا من النوع:

type Matcher<T extends number | boolean> = T extends number ? /\d+/ : /true|false/;
function fn<T extends number | boolean(arg: T, s: Matcher<T>) {
  type R = Matcher<T>
  if (R.test(arg)) {
      // ...
  }
}
fn(10, "10");
fn(false, "false");

يحظر التقاطعات

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

type Regex_1 = / ... /;
type Regex_2 = / ... /;
type NonRegex = { ... };
type test_4 = Regex_1 & NonRegex;// compile time error

لا يمكن أن يخطئ TypeScript في عمليات إنشاء التقاطعات ، لذلك لن يكون هذا جزءًا من أي تصميم نهائي.

بيئة العمل

بشكل عام ، أبرز ما لدينا هو أننا نريد شيئًا لا تكتب فيه نفس RegExp مرتين (مرة واحدة في مساحة القيمة ، ومرة ​​في مساحة الكتابة).

نظرًا للمخاوف المذكورة أعلاه بشأن الكتابة المنبعثة ، فإن الحل الأكثر واقعية هو أن تكتب التعبير في مساحة القيمة:

// Probably put this in lib.d.ts
type PatternOf<T extends RegExp> = T extends { test(s: unknown): s is infer P } ? P : never;

const ZipCode = /^\d\d\d\d\d$/;
function map(z: PatternOf<typeof ZipCode>) {
}

map('98052'); // OK
map('Redmond'); // Error

لا يزال بإمكانك كتابة RegExp في مساحة النوع ، بالطبع ، ولكن لن يكون هناك التحقق من صحة وقت التشغيل وسيتطلب أي استخدام غير حرفي إعادة اختبار أو تأكيد:

function map(z: /^\d\d\d\d\d$/) { }
map('98052'); // OK
map('Redmond'); // Error

function fn(s: string) {
    map(s); // Error
    // typo
    if (/^\d\d\d\d$/.test(s)) {
        // Error, /^\d\d\d\d$/ is not assignable to /^\d\d\d\d\d$/
        map(s);
    }

    if (/^\d\d\d\d\d$/.test(s)) {
        // OK
        map(s);
    }
}

جمع وتوضيح وقائع الاستخدام

بالنسبة لنوع جديد من النوع ، نود بشكل مثالي رؤية عدة أمثلة حيث:

  • لا يوجد بديل أفضل للمشكلة التي يتم حلها (بما في ذلك البدائل المعقولة التي لم يتم حلها بعد)
  • تحدث المشكلة مع تكرار ذي مغزى في قواعد البرمجة الحقيقية
  • الحل المقترح يحل هذه المشكلة بشكل جيد

التحقق من صحة وقت الترجمة من Literals

يتضمن هذا الخيط مجموعة متنوعة من حالات الاستخدام ؛ كانت الأمثلة الملموسة أكثر ندرة. من المثير للقلق أن العديد من هذه الأمثلة لا يبدو أنها كاملة - فهم يستخدمون RegExp يرفض المدخلات الصالحة.

  • لون الخط - AFAIK أي شيء يقبل الألوان السداسية يقبل أيضًا "أبيض" أو "أزرق سماوي". يؤدي هذا أيضًا إلى رفض بناء الجملة rgb(255, 0, 0) بشكل غير صحيح.
  • SSN و Zip وما إلى ذلك - حسنًا ، ولكن لماذا توجد رموز

    • عدد صحيح - رفض بشكل غير صحيح "3e5"

    • البريد الإلكتروني - تعتبر هذه عادة فكرة سيئة . مرة أخرى ، على الرغم من ذلك ، هناك سلسلة حرفية لعنوان البريد الإلكتروني في التعليمات البرمجية الخاصة بك ؟

    • مواصفات CSS Border - يمكنني أن أصدق أن مكتبة مستقلة يمكن أن توفر RegEx دقيقًا لوصف DSL الذي تدعمه هي نفسها

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

    • تنسيقات التاريخ - كيف / لماذا؟ Date لديه مُنشئ لهذا ؛ إذا كان الإدخال يأتي من خارج وقت التشغيل ، فأنت تريد فقط نوعًا رمزيًا

    • URI - يمكنك أن تتخيل أن fetch سيحدد host لعدم وجوده مع http(s?):

المهام: الرجاء مساعدتنا من خلال تحديد وظائف المكتبة الحقيقية التي يمكن أن تستفيد من أنواع RegExp والتعبير الفعلي الذي تستخدمه.

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

تنفيذ عمليات التحقق من وقت التشغيل

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

يرحب بالأدلة المضادة لهذا التقييم.

مفاتيح الملكية / مفهرسات سلسلة Regex

تعامل بعض المكتبات الكائنات وفقًا لأسماء الخصائص. على سبيل المثال ، في React نريد تطبيق أنواع على أي خاصية يبدأ اسمها بـ aria- :

interface IntrinsicElements {
    // ....
    [attributeName: /aria-\w+/]: number | string | boolean;
}

هذا مفهوم متعامد بشكل فعال (يمكننا إضافة أنواع Regex دون إضافة مفاتيح خصائص Regex ، والعكس صحيح).

TODO (أنا أو أي شخص آخر): افتح إصدارًا منفصلاً لهذا الغرض.

المقترحات المتنافسة

الأنواع الاسمية أو الموسومة

لنفترض أن لدينا أنواعًا اسمية / معلمة من نوع ما:

type ZipCode = make_unique_type string;

يمكنك بعد ذلك كتابة دالة

function asZipCode(s: string): ZipCode | undefined {
    return /^\d\d\d\d\d$/.test(s) ? (s as ZipCode) : undefined;
}

في هذه المرحلة ، هل تحتاج حقًا إلى أنواع RegExp؟ راجع قسم فحص "وقت الترجمة" لمزيد من الأفكار.

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

type Password = /(IsPassword)?.*/;
type UnescapedString = /(Unescaped)?.*/;
declare function hash(p: Password): string;

const p: Password = "My security is g00d"; // OK
const e: UnescapedString = "<div>''</div>"; // OK
hash(p); // OK
hash(e); // Error
hash("correct horse battery staple"); // OK

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

غير القضايا

ناقشنا الجوانب التالية ونعتبرها ليست مانعة

قدرات المضيف

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

أداء

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

التصنيف الفرعي ( /\d*/ -> /.*/ ؟) ، الاتحاد ، التقاطع ، وعدم قابلية السكن

نظريًا ، /\d+/ هو نوع فرعي معروف من /.+/ . من المفترض أن توجد خوارزميات لتحديد ما إذا كان أحد RegExp يطابق مجموعة فرعية نقية من مجموعة أخرى (في ظل قيود معينة) ، ولكن من الواضح أنه سيتطلب تحليل التعبير. من الناحية العملية ، نحن موافقون بنسبة 100٪ على عدم تكوين RegExpes لعلاقات ضمنية من الأنواع الفرعية بناءً على ما تتطابق معه ؛ ربما هذا هو الأفضل.

ستعمل عمليات الاتحاد والتقاطع "خارج الصندوق" طالما تم تعريف علاقات التخصيص بشكل صحيح.

في نسخة مطبوعة على الآلة الكاتبة، عند نوعين البدائية "الاصطدام" في مفترق طرق، فإنها تقلل إلى never . عند تقاطع اثنين من RegExpes ، سنحتفظ بذلك على أنه /a/ & /b/ بدلاً من محاولة إنتاج RegExp جديد يطابق تقاطع التعبيرين. لن يكون هناك أي تخفيض إلى never سنحتاج إلى خوارزمية لإثبات أنه لا توجد سلسلة يمكن أن ترضي كلا الجانبين (هذه مشكلة موازية لتلك الموصوفة سابقًا بشأن: التصنيف الفرعي).

الخطوات التالية

للتلخيص ، الخطوات التالية هي:

  • قم بتقديم مشكلة منفصلة لمفاتيح الخصائص المسماة Regex ومفهرسات سلسلة regex AKA
  • احصل على حالات استخدام ملموسة ومعقولة للتحقق من صحة وقت الترجمة من القيم الحرفية للسلسلة

    • مثال: تحديد الوظائف في DefinitelyTyped أو المكتبات الأخرى التي يمكن أن تستفيد بشكل كبير من ذلك

  • فهم ما إذا كانت الأنواع الاسمية / الموسومة / ذات العلامات التجارية هي حل أكثر مرونة وقابل للتطبيق على نطاق واسع للتحقق غير الحرفي
  • حدد المكتبات التي تقدم RegExes للتحقق من الصحة بالفعل

حالة الاستخدام: Hyperscript (https://github.com/hyperhype/hyperscript) مثل الوظائف
عادةً ما تُسمى دالة النص الفائق مثل h('div#some-id')
سيسمح مطابقة نمط regex-ish بتحديد نوع الإرجاع h والذي سيكون HTMLDivElement في حالة المثال.

إذا كان نظام النوع قادرًا على إضافة سلسلة حرفية ، فيمكن أن تكون أي خاصية CSS بشكل أساسي آمنة من النوع

declare let width: number;
declare let element: HTMLElement;

element.style.height = `${width}px`;
// ...or
element.style.height = `${width}%`;

يمكن التحقق من محددات CSS أيضًا ( element.class#id - صالح ، div#.name - غير صالح)

إذا كانت مجموعات الالتقاط ستعمل (بطريقة ما) ، فيمكن أن تكون طريقة Lodash get من النوع الآمن

var object = { 'a': [{ 'b': { 'c': 3 } }] };

_.get(object, 'a[0].b.c');

قد يكون هذا شيئًا أيضًا:

interface IOnEvents {
  [key: PatternOf</on[a-z]+/>]: (event: Event) => void;
}

interface IObservablesEndsOn$ {
  [key: PatternOf</\$$/>]: Observable<any>;
}

حالة الاستخدام: Hyperscript (hyperhype / hyperscript) مثل الدوال

كيف سيبدو هذا التعبير العادي ، أو ما هو التحقق من الصحة الذي سيوفره؟ هل هذا من أجل التحميل الزائد للوظيفة المستندة إلى regex؟

FWIW تقبل المكتبة أسماء علامات التمييز وكذلك الوظائف على أسماء علامات تمييز عشوائية

> require("hyperscript")("qjz").outerHTML
'<qjz></qjz>'

كما أنه يقبل مزجًا غير محدود بين قيم الفئة والمعرف

> require("hyperscript")("baz.foo#bar.qua").outerHTML
'<baz class="foo qua" id="bar"></baz>'

يمكن التحقق من محددات CSS أيضًا

لا يمكن التحقق من محددات CSS

كيف سيبدو هذا التعبير العادي ، أو ما هو التحقق من الصحة الذي سيوفره؟ هل هذا من أجل التحميل الزائد للوظيفة المستندة إلى regex؟

ليس OP ، لكنني أفترض ، نعم ، شيء من هذا القبيل الزائدة HTMLDocument#createElement() ، على سبيل المثال:

// ...
export declare function h(query: /^canvas([\.#]\w+)*$/): HTMLCanvasElement;
// ...
export declare function h(query: /^div([\.#]\w+)*$/): HTMLDivElement;
// ...

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

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

المهام: الرجاء مساعدتنا من خلال تحديد وظائف المكتبة الحقيقية التي يمكن أن تستفيد من أنواع RegExp والتعبير الفعلي الذي تستخدمه.

حالة الاستخدام الخاصة بي هي الحالة التي شرحتها في هذا التعليق في مكتبة CCXT حيث لدي سلاسل تمثل TickerSymbol s. لا أهتم حقًا إذا تم فحصها بحثًا عن نمط regex ، لكنني أريد أن يتم معاملتها على أنها أنواع فرعية من string لذلك أحصل على مهام أكثر صرامة ، وفحص نوع المعلمة ، وما إلى ذلك. أن يكون مفيدًا جدًا عندما أقوم ببرمجة وظيفية ، حيث يمكنني بسهولة تتبع رموز TickerS ، والعملات ، والأصول ، وما إلى ذلك في وقت التجميع حيث تكون في وقت التشغيل مجرد سلاسل عادية.

omidkrad يبدو أنك بحاجة إلى أنواع اسمية ، وليس أنواعًا تم التحقق من صحتها.

@ m93a في حالتي ، سأكون على ما يرام مع الأنواع الاسمية ، ولكن بالنسبة لنفس حالة الاستخدام ، يمكنك استخدام أنواع تم التحقق من صحة regex للتحقق من النوع الأكثر صرامة والتوثيق الذاتي لأنواع السلسلة.

يمكن التحقق من محددات CSS أيضًا

لا يمكن التحقق من محددات CSS

حسنًا ، إذا كان regexp سيمكننا من تجميعها معًا ، فيمكننا نسخ regexes CSS ... ، أليس كذلك؟

نموذج كائن (مسودة) CSS مكتوب

https://drafts.css-houdini.org/css-typed-om/

https://developers.google.com/web/updates/2018/03/cssom

يحتمل أن يخفف من الرغبة في استخدام نموذج CSS من النوع سلسلة.

el.attributeStyleMap.set('padding', CSS.px(42));
const padding = el.attributeStyleMap.get('padding');
console.log(padding.value, padding.unit); // 42, 'px'

RyanCavanaugh بالنسبة إلى Mithril على وجه الخصوص ، يتم استخراج اسم العلامة من خلال مجموعة الالتقاط في ^([^#\.\[\]]+) (الافتراضي هو "div" ) ، لكن مطابقة ^(${htmlTagNames.join("|")}) ستكون كافية لأغراضنا. ومن ثم ، باستخدام اقتراحي ، سيكون هذا كافيًا لأغراضي:

type SelectorAttrs = "" | `#${string}` | `.${string}`;

type GetTagName<T extends string> =
    T extends SelectorAttrs ? "div" :
    T extends `${keyof HTMLElementTagNameMap & (infer Tag)}${SelectorAttrs}` ? T :
    string;

بالنسبة للأحداث والسمات ، يمكننا التبديل إلى هذه الأنواع التي تم إبطالها مرة واحدة:

type EventsForElement<T extends Element> =
    T extends {addEventListener(name: infer N, ...args: any[]): any} ? N : never;

type MithrilEvent<E extends string> =
    (E extends EventsForElement<T> ? HTMLElementEventMap[E] : Event) &
    {redraw?: boolean};

type Attributes<T extends Element> =
    LifecycleAttrs<T> &
    {[K in `on${string}` & not LifecycleAttrs<T>](
        ev: K extends `on${infer E}` ? MithrilEvent<E> : never
    ): void | boolean} &
    {[K in keyof T & not `on${string}`]: T[K]} &
    {[K in string & not keyof T & not `on${string}`]: string};

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


لا أعرف أي طريقة للقيام بذلك باستخدام أنواع regexp الخالصة. أنا لا أريد أن أشير إلى ذلك.

المهام: الرجاء مساعدتنا من خلال تحديد وظائف المكتبة الحقيقية التي يمكن أن تستفيد من أنواع RegExp والتعبير الفعلي الذي تستخدمه.

لدى عازمة نوع إرجاع مختلف بناءً على ما يتم تقديمه كسلسلة تصف نوع الاستجابة المتوقعة ، على سبيل المثال

bent('json')('https://google.com') // => Promise<JSON>
bent('buffer')('https://google.com') // => Promise<Buffer | ArrayBuffer>
bent('string')('https://google.com') // => Promise<String>

كما أنه يقبل بعض الوسيطات الأخرى ، مثل الطريقة وعنوان url كسلاسل ، ولكن يمكن أن تظهر في أي موضع ، لذلك إذا حاولنا استخدام النقابات لوصف كل نوع الإرجاع ( 'json' | 'buffer' | 'string' ) ، فسيكون هذا أمرًا خافتًا بدلاً من ذلك إلى string عند دمجه مع عنوان url وأنواع الطريقة في الاتحاد ، مما يعني أنه لا يمكننا تلقائيًا استنتاج نوع الإرجاع بناءً على النوع المعطى في الاستدعاء الأول.

Ovyerus كيف يمكن أن تساعدك أنواع regex هناك؟ ماذا تتوقع أن تكتب؟ يمكنك نمذجة شيء مشابه لسلوك عازمة مع الأحمال الزائدة أو الأنواع الشرطية.

type BentResponse<Encoding> = Promise<
    Encoding extends "json" ? MyJsonType :
    Encoding extends "buffer" ? Buffer | ArrayBuffer :
    Encoding extends "string" ? string :
    Response
>;

declare function bent<T extends string>(urlOrEncoding: T): (url: string) => BentResponse<T>;

http://www.typescriptlang.org/play/index.html#code/C4TwDgpgBAQhB2wBKEDOYD29UQDwFF4BjDAEwEt4BzAPigF4oAFAJwwFtydcBYAKCiCohEhWpQIAD2AJSqKACIAVqiwKoAfigBZEAClV8ACrhoALn5DhxMpSoTps + QoBGAVwBmHiC3VaYnt4sUAA + UACCLCwAhiABXj5QFgJCIrbiUjLwcoqowCx2flB5BeLJVijoWDj8NADc-PykEEQANtEs0B5uxMDkWFAuCMC4Rg5ZOSV2NAAUbiytAPIsaWJUZlBGAJQbcwsbU9RbDHRwiJWY2HhG9Y18lDIsHtFE0PFBUADeUAD67gksDbReAgKAAX34Dx8z1eOn0hhMkC + vxUWCBIPBdxm0VQIGIUBmx3odE + liErQgwCgkg2ugMWER0EY0QA7tFyFShogZspDAotjyABbAYBgVBmAD0Eqk0XYYApADoSOx + Q0 + GCBVsgA

أوه ، لم أكن واضحًا ، آسف ، أعتقد أن مشكلتي كانت أقرب إلى مطابقة http(s): في بداية سلسلة لاكتشاف عنوان URL الأساسي.

توقيع بنت هو أكثر على غرار

type HttpMethods = 'GET' | 'PATCH' | ...
type StatusCode = number;
type BaseUrl = string; // This is where I would ideally need to see if a string matches http(s):
type Headers = { [x: string]: any; };

type Options = HttpMethods | StatusCode | BaseUrl | Headers;

function bent(...args: Options[]): RequestFunction<RawResponse>
function bent(...args: (Options | 'json')[]): RequestFunction<JSON>
// and so on

ومع ذلك ، فإن وجود BaseUrl كسلسلة يمتص HttpMethods ويعيد اتحادات النوع ، والتي تنتهي بـ string . إن وجودها كسلسلة يتطابق أيضًا "بشكل غير صحيح" مع كيفية عمل الانحناء ، حيث إنه يتحقق من وجود ^http: أو ^https: لتحديد ما يجب استخدامه كعنوان url الأساسي.

إذا كان لدينا أنواع regex ، فيمكنني تعريف BaseUrl على أنه type BaseUrl = /^https?:/ ، وهذا بشكل مثالي من شأنه أن يتحقق بشكل صحيح من السلاسل التي ليست طريقة HTTP أو ترميز استجابة ، فضلاً عن عدم استيعابها في string نوع.

بالضبط ، أنا نفس الشيء.

-
بروكوب سيميك

في 20 أكتوبر 2019 الساعة 03:23:30 ، مايكل ميتشل ([email protected])
كتب:

لم أكن واضحًا ، آسف لأني أعتقد أن مشكلتي كانت على غرار
مطابقة http (s): في بداية سلسلة لاكتشاف عنوان URL الأساسي.

توقيع بنت هو أكثر على غرار

اكتب HttpMethods = 'الحصول على' | "باتش" | ... اكتب StatusCode = رقم ؛ اكتب BaseUrl = سلسلة ؛ // هذا هو المكان الذي سأحتاج فيه بشكل مثالي لمعرفة ما إذا كانت السلسلة تطابق http (s): اكتب Headers = {[x: string]: any؛ } ؛
اكتب خيارات = HttpMethods | كود الحالة | BaseUrl | رؤوس
وظيفة عازمة (... args: Options []): RequestFunctionدالة عازمة (... أرجس: (خيارات | 'json') []): RequestFunction// وما إلى ذلك وهلم جرا

ومع ذلك ، فإن استخدام BaseUrl كسلسلة يمتص HttpMethods ويعيدها
اكتب النقابات ، والتي تنتهي كسلسلة فقط. وجودها فقط كسلسلة
يطابق أيضًا "بشكل غير صحيح" كيفية عمل الانحناء ، لأنه يتحقق من التواجد
من ^ http: أو ^ https: لتحديد ما يجب استخدامه كأساس
عنوان url.

إذا كانت لدينا أنواع regex ، فيمكنني تعريف BaseUrl كنوع BaseUrl = / ^ https؟: /،
وهذا بشكل مثالي من شأنه التحقق بشكل صحيح من السلاسل التي ليست طريقة HTTP أو
ترميز الاستجابة ، وكذلك عدم امتصاصها في نوع السلسلة.

-
أنت تتلقى هذا لأنك مشترك في هذا الموضوع.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/microsoft/TypeScript/issues/6579؟
أو إلغاء الاشتراك
https://github.com/notifications/unsubscribe-auth/ABJ3U4PHBXO4766LK7P7UXDQPOXJFANCNFSM4BZLAVSA
.

كان التفكير في حالة الاستخدام هو اكتشاف أنواع المعلمات لوظيفة ما.

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

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

DanielRosenwasser فيما يلي مثال على الكود الذي نود أن http://www.typescriptlang.org/play/index.html#code/C4TwDgpgBAqjCSARKBeKBnYAnAlgOwHMBYAKFIGMAbAQ3XVnQiygG9SoOoxcA3a4aJn45yULBGoATAPZ5KIKAFtq + GIywAuWAmRoA5NQCcEAAwBGQwCMArAFoAZtYBMAdlsAWcvYActw2ZM7JzNJF28TCCcANgtLPXZOAHpErl5 + QWBhUXEpWXklFTw1Ji04JFQoMycAZihkqAA5AHkAFSgAQQAZTqaAdQBRRASOeu4cPgEMTOARMQkZOQVlVXVSnQq9PGlgW2pbAFd9nEk9OpSAZQAJJphO5Ga2gCF + ju6 + واه ++ برميل-oAlYZQciyTBYfbkYDSLAACkBnC4 + 0slFmxzWSAANHDOGBEcjRJYsNQ8JItKD8ARMSR4fCcUjZuocNRKFo8PtFJYmJTqdjcbNyDkBJJHiA0boGEwAHTLIrqACUrFICQAvqQVSQgA

yannickglt يبدو أنك تريد نوعًا

// OK
someFunc('a9e019b5-f527-4cf8-9105-21d780e2619b');
// Also OK, but probably really bad
someFunc('a9e019b5-f527-4cf8-9106-21d780e2619b');
// Error
someFunc('bfe91246-8371-b3fa-3m83-82032713adef');

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

لذا فإن الجمع بين ميزة 3.7 Assertion Functions والميزة nominal يمكنها القيام بذلك (؟)

nominal UUID = string

function someFunc(uuid: any): asserts uuid is UUID {
  if (!UUID_REGEX.test(uuid)) {
    throw new AssertionError("Not UUID!")
  }
}

class User {
  private static readonly mainUser: UUID = someFunc('a9e019b5-f527-4cf8-9105-21d780e2619b')
  // private static readonly mainUser: UUID = someFunc(123) // assertion fails
  // private static readonly mainUser: UUID = someFunc('not-a-uuid') // assertion fails
  constructor(
    public id: UUID,
    public brand: string,
    public serial: number,
    public createdBy: UUID = User.mainUser) {

  }
}

هل سيفشل هذا أيضا؟

new User('invalid-uuid', 'brand', 1) // should fail
new User('invalid-uuid' as UUID, 'brand', 1) // 🤔 

بعد التفكير لفترة من الوقت ، أرى مشكلة في الحل المقترح
يؤدي asserts فقط إلى حدوث خطأ في وقت التشغيل -> 👎
قد يؤدي Regex-Validation إلى حدوث خطأ في وقت الترجمة -> 👍
خلاف ذلك ، فإن هذا الاقتراح لا معنى له

يحرر:
مشكلة أخرى: لا يقوم someFunc(uuid: any): asserts uuid is UUID بإرجاع UUID ، بل يقوم بإلقاء أو إرجاع is UUID -> true .
لذلك لا يمكنني استخدام هذه الوظيفة لتعيين UUID بهذه الطريقة إلى mainUser

RyanCavanaugh نريد أن تتم كتابتها بشكل صحيح لـ Mithril:

// <div id="hello"></div>
m("div#hello", {
    oncreate(vnode) { const dom: HTMLDivElement = vnode.dom },
})

// <section class="container"></section>
m("section.container", {
    oncreate(vnode) { const dom: HTMLElement = vnode.dom },
})

// <input type="text" placeholder="Name">
m("input[type=text][placeholder=Name]", {
    oncreate(vnode) { const dom: HTMLInputElement = vnode.dom },
})

// <a id="exit" class="external" href="https://example.com">Leave</a>
m("a#exit.external[href='https://example.com']", {
    oncreate(vnode) { const dom: HTMLAnchorElement = vnode.dom },
}, "Leave")

// <div class="box box-bordered"></div>
m(".box.box-bordered", {
    oncreate(vnode) { const dom: HTMLDivElement = vnode.dom },
})

// <details></details> with `.open = true`
m("details[open]", {
    oncreate(vnode) { const dom: HTMLDetailsElement = vnode.dom },
})

// alias for `m.fragment(attrs, ...children)`
m("[", {
    oncreate(vnode) { const dom: HTMLElement | SVGElement = vnode.dom },
}, ...children)

نريد رفضًا ثابتًا لهذه:

// selector must be non-empty
m("")

// incomplete class
m("div.")

// incomplete ID
m("div#")

// incomplete attribute
m("div[attr=")

// not special and doesn't start /[a-z]/i
m("@foo")

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

// event handers must be functions
m("div[onclick='return false']")

// `select.selectedIndex` is a number
m("select[selectedIndex='not a number']")

// `input.form` is read-only
m("input[type=text][form='anything']")

// `input.spellcheck` is a boolean, this evaluates to a string
// (This is a common mistake, actually.)
m("input[type=text][spellcheck=false]")

// invalid tag name for non-custom element
m("sv")

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

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

أتمنى أن يساعدك هذا!

isiahmeadows طريقة أفضل لك لاستخدام بعض أشكال

m(mt.div({ attr1: 'val1' }))

@ anion155 هناك طرق أخرى للوصول إلى هناك أيضًا ، ولكن هذا يتعلق بكتابة مكتبة تم تصميم واجهة برمجة التطبيقات الخاصة بها بواسطة مؤلفها الأصلي في عام 2014. إذا كنت أصمم واجهة برمجة التطبيقات الخاصة بها الآن ، فمن المحتمل أن أستخدم m("div", {...attrs}, ...children) مع لا شيء من السكر عالي الكتابة (أسهل في الكتابة ، وأبسط بكثير في المعالجة) ، ولكن فات الأوان الآن لفعل الكثير حيال ذلك.

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

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

بخصوص "الدقة" (يا رجل ، أحب هذه الكلمة) ،
لا أعتقد أننا يجب أن نقلق بشأنه كثيرًا.

نظام الكتابة مكتمل بالفعل.
هذا يعني في الأساس أنه يمكننا أن نكون دقيقين للغاية بشأن الكثير من الأشياء.
(مثل ، نمذجة كل SQL؟ قابس وقح = P)

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

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


حالة الاستخدام

بعيدًا عن رأسي ، يمكنني التفكير في أحد الأمثلة الحديثة. (هناك حفنة أكثر لكنني كائن نسي)

عند التكامل مع Stripe's API (نظام أساسي لمعالجة الدفع) ، فإنهم يستخدمون ch_ مقابل charge المعرفات المرتبطة ، re_ لـ refund المعرفات ، إلخ.

كان من الجيد ترميزها بـ PatternOf</^ch_.+/> و PatternOf</^re_.+/> .

بهذه الطريقة ، عند إجراء أخطاء مطبعية مثل ،

charge.insertOne({ stripeChargeId : someObj.refundId });

سأحصل على خطأ ،

Cannot assign `PatternOf</^re_.+/>` to `PatternOf</^ch_.+/>`

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

أيضًا ، تعد الأنواع الموسومة رائعة للأنواع الوهمية.
الأنواع الاسمية ليست مفيدة أبدًا.
(حسنًا ، قد أكون متحيزًا. إنها مفيدة فقط بسبب unique symbol لكني أحب أن أعتقد أنني لست مخطئًا تمامًا.)

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


مقارنة

أدناه ، سأقارن ما يلي ،

  • أنواع أنماط السلسلة / أنواع السلاسل التي تم التحقق من صحتها
  • الأنواع الاسمية
  • أنواع العلامات الهيكلية

يمكننا أن نتفق جميعًا على أن نمط "ValueObject" هو الحل الأسوأ ، ولا نتعبه في المقارنات ، أليس كذلك؟


أنواع نمط السلسلة

const stripeChargeIdRegex = /^ch_.+/;
const stripeRefundIdRegex = /^re_.+/;

type StripeChargeId = PatternOf<typeof stripeChargeIdRegex>;
type StripeRefundId = PatternOf<typeof stripeRefundIdRegex>;

declare function takesStripeChargeId (stripeChargeId : StripeChargeId) : void;

declare const str : string;
takesStripeChargeId(str); //Error
if (stripeChargeIdRegex.test(str)) {
  takesStripeChargeId(str); //OK
}
if (stripeRefundIdRegex.test(str)) {
  takesStripeChargeId(str); //Error
}

declare const stripeChargeId : StripeChargeId;
declare const stripeRefundId : StripeRefundId;
takesStripeChargeId(stripeChargeId); //OK
takesStripeChargeId(stripeRefundId); //Error

takesStripeChargeId("ch_hello"); //OK
takesStripeChargeId("re_hello"); //Error

انظر إلى ذلك.

  • مثالي لسلسلة حرفية.
  • ليس سيئًا جدًا بالنسبة إلى string غير الحرفية.

الأنواع الاسمية ...

const stripeChargeIdRegex = /^ch_.+/;
const stripeRefundIdRegex = /^re_.+/;

type StripeChargeId = make_unique_type string;
type StripeRefundId = make_unique_type string;

function isStripeChargeId (str : string) : str is StripeChargeId {
  return stripeChargeIdRegex.test(str);
}
function isStripeRefundId (str : string) : str is StripeRefundId {
  return stripeRefundIdRegex.test(str);
}

declare function takesStripeChargeId (stripeChargeId : StripeChargeId) : void;

declare const str : string;
takesStripeChargeId(str); //Error
if (isStripeChargeId(str)) {
  takesStripeChargeId(str); //OK
}
if (isStripeRefundId(str)) {
  takesStripeChargeId(str); //Error
}

declare const stripeChargeId : StripeChargeId;
declare const stripeRefundId : StripeRefundId;
takesStripeChargeId(stripeChargeId); //OK
takesStripeChargeId(stripeRefundId); //Error

takesStripeChargeId("ch_hello"); //Error? Ughhhh
takesStripeChargeId("re_hello"); //Error

takesStripeChargeId("ch_hello" as StripeChargeId); //OK, BUT UNSAFE
takesStripeChargeId("re_hello" as StripeChargeId); //OK, BUT WAIT! I MESSED UP

const iKnowThisIsValid = "ch_hello";
if (isStripeChargeId(iKnowThisIsValid)) {
  takesStripeChargeId(iKnowThisIsValid); //OK
} else {
  throw new Error(`Wat? This should be valid`);
}

function assertsStripeChargeId (str : string) : asserts str is StripeChargeId {
  if (!isStripeChargeId(str)) {
    throw new Error(`Expected StripeChargeId`);
  }
}
assertsStripeChargeId(iKnowThisIsValid);
takesStripeChargeId(iKnowThisIsValid); //OK

function makeStripeChargeIdOrError (str : string) : StripeChargeId {
  assertsStripeChargeId(str);
  return str;
}
takesStripeChargeId(makeStripeChargeIdOrError("ch_hello")); //OK
takesStripeChargeId(makeStripeChargeIdOrError("re_hello")); //OK, compiles, throws during run-time... Not good

انظر إلى ذلك.

  • رهيب لسلسلة حرفية.
  • بعد التغلب على عقبة السلسلة الحرفية ، ليس الأمر سيئًا للغاية ... أليس كذلك؟

لكن حالة الاستخدام الرئيسية لهذا الاقتراح هي السلاسل الحرفية.
لذا ، هذا بديل رهيب.


أنواع العلامات الهيكلية ...

لا تختلف أنواع العلامات الهيكلية كثيرًا عن الأنواع الاسمية ...

const stripeChargeIdRegex = /^ch_.+/;
const stripeRefundIdRegex = /^re_.+/;

type StripeChargeId = string & tag { stripeChargeId : void };
type StripeRefundId = string & tag { stripeRefundId : void };

function isStripeChargeId (str : string) : str is StripeChargeId {
  return stripeChargeIdRegex.test(str);
}
function isStripeRefundId (str : string) : str is StripeRefundId {
  return stripeRefundIdRegex.test(str);
}

declare function takesStripeChargeId (stripeChargeId : StripeChargeId) : void;

declare const str : string;
takesStripeChargeId(str); //Error
if (isStripeChargeId(str)) {
  takesStripeChargeId(str); //OK
}
if (isStripeRefundId(str)) {
  takesStripeChargeId(str); //Error
}

declare const stripeChargeId : StripeChargeId;
declare const stripeRefundId : StripeRefundId;
takesStripeChargeId(stripeChargeId); //OK
takesStripeChargeId(stripeRefundId); //Error

takesStripeChargeId("ch_hello"); //Error? Ughhhh
takesStripeChargeId("re_hello"); //Error

takesStripeChargeId("ch_hello" as StripeChargeId); //OK, BUT UNSAFE
takesStripeChargeId("re_hello" as StripeChargeId); //OK, BUT WAIT! I MESSED UP

const iKnowThisIsValid = "ch_hello";
if (isStripeChargeId(iKnowThisIsValid)) {
  takesStripeChargeId(iKnowThisIsValid); //OK
} else {
  throw new Error(`Wat? This should be valid`);
}

function assertsStripeChargeId (str : string) : asserts str is StripeChargeId {
  if (!isStripeChargeId(str)) {
    throw new Error(`Expected StripeChargeId`);
  }
}
assertsStripeChargeId(iKnowThisIsValid);
takesStripeChargeId(iKnowThisIsValid); //OK

function makeStripeChargeIdOrError (str : string) : StripeChargeId {
  assertsStripeChargeId(str);
  return str;
}
takesStripeChargeId(makeStripeChargeIdOrError("ch_hello")); //OK
takesStripeChargeId(makeStripeChargeIdOrError("re_hello")); //OK, compiles, throws during run-time... Not good

انظر إلى ذلك.

  • رهيب لسلسلة حرفية.
  • بعد التغلب على عقبة السلسلة الحرفية ، ليس الأمر سيئًا للغاية ... أليس كذلك؟

لكن حالة الاستخدام الرئيسية لهذا الاقتراح هي السلاسل الحرفية.
لذا ، هذا بديل رهيب.

أيضًا ، مثال نوع العلامة الهيكلية هذا هو نسخ ولصق حرفي (ha ، pun) لمثال النوع الاسمي .

الاختلاف الوحيد هو في كيفية التصريح عن الأنواع StripeChargeId و StripeRefundId .

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


استنتاج

هذا مجرد استنتاج لهذا التعليق! ليست خاتمة لأفكاري الشاملة!

تعد أنواع أنماط السلسلة / أنواع السلاسل التي تم التحقق من صحتها من خلال regex مفتعلة بشكل كبير قد أثبتت ذلك بشكل كافٍ.


الخلاصة (إضافي)

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

أمثلة لأخذ مجموعة فرعية من الأنواع البدائية ،

  • string حرفية
  • number Literals (باستثناء NaN, Infinity, -Infinity )
  • boolean حرفية
  • bigint حرفية
  • حتى unique symbol يأخذ مجموعة فرعية من symbol

من الأمثلة المذكورة أعلاه ، فقط boolean "محدود بما فيه الكفاية". لها قيمتان فقط.
يشعر المطورون بالرضا عن وجود قيم حرفية false true و false لأنه لا يوجد شيء آخر يمكن طلبه.


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

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

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


النوع bigint هو في الأساس لانهائي ، مقيد بالذاكرة فقط.

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


النوع string لا نهائي بشكل أساسي ، مقيد بالذاكرة فقط.

وهذا هو سبب شيوع مشكلة نوع السلسلة من نوع نمط السلسلة / regex-التحقق من صحة هذه المشكلة.

يعد تحديد regex أحد الأفكار الأكثر شيوعًا / الطبيعية التي يجب طرحها على شخص ما عندما يحتاج إلى تحديد مجموعة فرعية كبيرة محدودة / لانهائية من مجموعة لانهائية.


النوع symbol ... إنه أيضًا لانهائي. وأيضًا غير محدود ، إلى حد كبير.

لكن عناصر النوع symbol كلها غير مرتبطة ببعضها البعض تقريبًا ، من جميع النواحي تقريبًا. وبالتالي ، لم يقم أحد بسؤاله ، "هل يمكنني الحصول على طريقة لتحديد مجموعة فرعية كبيرة محدودة / لانهائية من symbol ؟".

بالنسبة لمعظم الناس ، هذا السؤال لا معنى له حتى. لا توجد طريقة مجدية للقيام بذلك (أليس كذلك؟)


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

  • يجب أن تكون الأحرف الحرفية من النوع الصحيح قابلة للتخصيص دون مزيد من العمل

لحسن الحظ ، TS عاقل بما يكفي للسماح بذلك.

تخيل أنك غير قادر على تمرير false إلى (arg : false) => void !

  • طرق مدمجة للتضييق

    في الوقت الحالي ، بالنسبة لهذه القيم الحرفية ، لدينا == & === كطرق مضمنة للتضييق.

    تخيل أنك بحاجة إلى كتابة نوع جديد من الحراس لكل حرف!

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

بيئة العمل

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

تنطبق هذه الوسائط أيضًا على https://github.com/microsoft/TypeScript/issues/15480 .


التوافق عبر المكتبات

الأنواع الاسمية هي الأسوأ في التوافق عبر المكتبات.
يشبه استخدام unique symbol في مكتبتين ومحاولة جعلهما يعملان معًا.
انها ببساطة لا يمكن القيام به.
تحتاج إلى استخدام حارس من النوع المعياري ، أو عامل الثقة ( as ).

ستحتاج إلى المزيد من النماذج المعيارية لحارس التأكيد أيضًا.

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


بالنسبة للأنواع الهيكلية ، إذا كانت المكتبة A تمتلك ،

//Lowercase 'S'
type StripeChargeId = string & tag { stripeChargeId : void };

والمكتبة B بها ،

//Uppercase 'S'
type StripeChargeId = string & tag { StripeChargeId : void };

//Or
type StripeChargeId = string & tag { isStripeChargeId : true };

//Or
type StripeChargeId = string & tag { stripe_charge_id : void };

ستحتاج بعد ذلك إلى حارس من النوع المعياري ، أو عامل Trust-me ( as ).

ستحتاج إلى المزيد من النماذج المعيارية لحارس التأكيد أيضًا.

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


بالنسبة لأنواع أنماط السلسلة ، إذا كانت المكتبة A تمتلك ،

type stripeChargeIdRegex = /^ch_.+/;
type StripeChargeId = PatternOf<typeof stripeChargeIdRegex>;

والمكتبة B لديها ،

//Extra dollar sign at the end
type stripeChargeIdRegex = /^ch_.+$/;
type StripeChargeId = PatternOf<typeof stripeChargeIdRegex>;

//Or,
type stripeChargeIdRegex =/^ch_[a-zA-Z0-9]$/;
type StripeChargeId = PatternOf<typeof stripeChargeIdRegex>;

//Or,
type stripeChargeIdRegex =/^ch_[A-Za-z0-9]$/;
type StripeChargeId = PatternOf<typeof stripeChargeIdRegex>;

افترض أن المكتبتين تنتج دائمًا سلاسل StripeChargeId تفي بمتطلبات كلتا المكتبتين. المكتبة A هي مجرد "كسول" مع التحقق من صحتها. والمكتبة B "أكثر صرامة" فيما يتعلق بالتحقق من صحتها.

ثم ، إنه نوع من الإزعاج. لكن ليس سيئا للغاية.
لأنه يمكنك فقط استخدام libraryB.stripeChargeIdRegex.test(libraryA_stripeChargeId) كحارس للطباعة. لا حاجة لاستخدام عامل الثقة ( as ).

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

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


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

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

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


التوافق عبر الإصدارات

كالعادة ، الأنواع الاسمية هي الأسوأ في توافق النسخ المتقاطعة.
أوه ، لقد صدمت مكتبتك برقعة ، أو نسخة ثانوية؟
نوع decalaration لا يزال هو نفسه؟
الرمز لا يزال هو نفسه؟
لا. إنها أنواع مختلفة.

image


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


لا تزال أنواع أنماط السلسلة قابلة للتخصيص ، عبر الإصدارات (حتى الإصدارات الرئيسية) ، طالما أن regex هو نفسه.

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

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

[يحرر]
في هذا التعليق ،
https://github.com/microsoft/TypeScript/issues/6579#issuecomment -243338433

شخص ما مرتبط بـ ،
https://bora.uib.no/handle/1956/3956

بعنوان "مشكلة التضمين للتعبيرات العادية"
[/يحرر]


المرجل

TODO (لكن يمكننا أن نرى أن أنواع أنماط السلسلة تحتوي على أقل قدر من المتغيرات)

الدعاء الحرفي

TODO (لكن يمكننا أن نرى أن أنواع أنماط السلسلة تدعم الاستدعاء الحرفي بشكل أفضل)

الدعاء غير الحرفي

TODO (لكن يمكننا أن نرى أن أنواع أنماط السلسلة تدعم الاستدعاء غير الحرفي بشكل أفضل)

المزيد بخصوص https://github.com/microsoft/TypeScript/issues/6579#issuecomment -542405537

لا يمكن أن يخطئ TypeScript في عمليات إنشاء التقاطعات ، لذلك لن يكون هذا جزءًا من أي تصميم نهائي.

لا أعرف لماذا أراد الناس حظر التقاطعات ، لكنك محق تمامًا في أن الحظر غير منطقي.


هكذا كسر كل احتجاج غير حرفي؟

حسنًا ، ليس كل احتجاج غير حرفي.

declare function foo (arg : PatternOf</a+/>) : void;
function bar (arg : PatternOf</a+/>) : void {
  //non-literal and does not break.
  foo(arg);
}
bar("aa"); //OK
bar("bb"); //Error
bar("" as string); //Error, I know this is what you meant by non-literal invocation

function baz (arg : "car"|"bar"|"tar") : void {
  bar(arg); //OK
}

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

هذا نوع من مثل قول أن القيم الحرفية للسلسلة سيئة لأن الاستدعاءات غير الحرفية الآن تفشل.
تتيح لك أنواع أنماط السلسلة / أنواع السلاسل التي تم التحقق من صحتها من خلال regex فقط تحديد اتحادات لعدد لا حصر له من القيم الحرفية للسلسلة.


أي استخدام غير حرفي يتطلب إعادة اختبار أو تأكيد:

لا أرى في ذلك مشكلة على الإطلاق.
إنه نفس الشيء مع الأنواع الاسمية / الموسومة في الوقت الحالي.
أو محاولة تمرير string إلى دالة تتوقع سلسلة حرفية.
أو محاولة تمرير نوع أوسع إلى نوع أضيق.

في هذه الحالة بالذات ، أظهرت أن const ZipCode = /^\d\d\d\d\d$/; و ZipCode.test(s) يمكن أن يعملوا كحارس نوع. سيساعد هذا بالتأكيد في بيئة العمل.


  • لا يوجد بديل أفضل للمشكلة التي يتم حلها (بما في ذلك البدائل المعقولة التي لم يتم حلها بعد)

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

  • تحدث المشكلة مع تكرار ذي مغزى في قواعد البرمجة الحقيقية

أه ... دعني أعود إليك على هذا ...

  • الحل المقترح يحل هذه المشكلة بشكل جيد

يبدو أن نوع نمط السلسلة المقترح جيد جدًا.


المهام: الرجاء مساعدتنا من خلال تحديد وظائف المكتبة الحقيقية التي يمكن أن تستفيد من أنواع RegExp والتعبير الفعلي الذي تستخدمه.

وجهة نظرك هي أن الأنواع الاسمية / الموسومة جيدة بما يكفي للاستخدام غير الحرفي.
لذلك ، فإن أي حالة استخدام تُطرح وتُظهر استخدامًا غير حرفي ليست جيدة بما يكفي ، لأن الأنواع الاسمية / الموسومة تغطيها.

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

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

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


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

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

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

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

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

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

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

مع أنواع أنماط السلسلة ، هناك سبب أقل لاستخدام as كل مكان وسيعرف كل من مؤلف المكتبة والمستهلكين المتلقين للمعلومات عن كسر التغييرات بسهولة أكبر.

(نوع من الرياح الطويلة ولكن أتمنى أن تكون بعض نقاطي قد وصلت).


أيضًا ، أكتب الكثير من اختبارات وقت التجميع (وأنا أعلم أن فريق TS يقوم بذلك أيضًا).

سيكون من الرائع أن أختبر أن string حرفية معينة

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


يبدو أن هذا يضعنا بسرعة على الطريق إلى وضع برج بابل ...

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

ومع ذلك ، فإن أنواع regexes / string-pattern لديها فرصة جيدة لعدم الوقوع في هذه المشكلة (نأمل ، بفضل التوحيد القياسي ، ومؤلفي المكتبات العقلاء).


تعديل

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

آه ... كان يجب أن أقرأ كل شيء أولاً قبل كتابة هذا ...

على أي حال ، لدي بعض الأمثلة معي ، حيث تكون أنواع أنماط السلسلة مفيدة.


مكتبة إعلان مسار HTTP

باستخدام هذه المكتبة ، يمكنك بناء كائنات إعلان مسار HTTP. يتم استخدام هذا الإعلان من قبل كل من العميل والخادم.

/*snip*/
createTestCard : f.route()
    .append("/platform")
    .appendParam(s.platform.platformId, /\d+/)
    .append("/stripe")
    .append("/test-card")
/*snip*/

هذه هي قيود .append() ،

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

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


اشياء اخرى

لدي أيضًا regexes للسلاسل السداسية العشرية ، سلاسل أبجدية رقمية.

لدي هذا أيضًا ،

const floatingPointRegex = /^([-+])?([0-9]*\.?[0-9]+)([eE]([-+])?([0-9]+))?$/;

أنا أرى هذا،

عدد صحيح - رفض "3e5" بشكل غير صحيح

لدي أيضًا هذا ، وهو ليس تعبيرًا عاديًا صحيحًا ولكنه يستخدم floatingPointRegex ،

function parseFloatingPointString (str : string) {
    const m = floatingPointRegex.exec(str);
    if (m == undefined) {
        return undefined;
    }
    const rawCoefficientSign : string|undefined = m[1];
    const rawCoefficientValue : string = m[2];
    const rawExponentSign : string|undefined = m[4];
    const rawExponentValue : string|undefined = m[5];

    const decimalPlaceIndex = rawCoefficientValue.indexOf(".");
    const fractionalLength = (decimalPlaceIndex < 0) ?
        0 :
        rawCoefficientValue.length - decimalPlaceIndex - 1;

    const exponentValue = (rawExponentValue == undefined) ?
        0 :
        parseInt(rawExponentValue) * ((rawExponentSign === "-") ? -1 : 1);

    const normalizedFractionalLength = (fractionalLength - exponentValue);
    const isInteger = (normalizedFractionalLength <= 0) ?
        true :
        /^0+$/.test(rawCoefficientValue.substring(
            rawCoefficientValue.length-normalizedFractionalLength,
            rawCoefficientValue.length
        ));
    const isNeg = (rawCoefficientSign === "-");

    return {
        isInteger,
        isNeg,
    };
}

لدي هذا التعليق أيضًا ،

/**
    Just because a string is in integer format does not mean
    it is a finite number.

    ```ts
    const nines_80 = "99999999999999999999999999999999999999999999999999999999999999999999999999999999";
    const nines_320 = nines_80.repeat(4);
    //This will pass, 320 nines in a row is a valid integer format
    integerFormatString()("", nines_320);
    //Infinity
    parseFloat(nines_320);
    ```
*/

مُنشئ RegExp

ومن المضحك أن المُنشئ RegExp سيستفيد من أنواع السلاسل التي تم التحقق من صحتها من خلال regex!

الآن ،

new(pattern: string, flags?: string): RegExp

ومع ذلك ، يمكن أن يكون لدينا ،

new(pattern: string, flags?: PatternOf</^[gimsuy]*$/>): RegExp

TL ؛ DR (يرجى قراءتها ، على الرغم من أنني بذلت الكثير من الجهد في هذا: صرخة:)

  • تعد أنواع أنماط السلسلة أكثر راحة من أنواع العلامات الاسمية / التركيبية

    • أقل متداخلة

  • تعد أنواع أنماط السلسلة أقل احتمالا من أنواع العلامات الاسمية / التركيبية لتصبح حالة برج بابل

    • خاصة مع فحوصات الأنواع الفرعية للتعبير العادي

  • أنواع أنماط السلسلة هي الطريقة الأكثر طبيعية لتعريف مجموعات فرعية كبيرة محدودة / لا نهائية من النوع string

    • قد يؤدي تقديم هذه الميزة إلى جعل الأشخاص يفكرون في تنسيقات السلاسل الصالحة لمكتباتهم عن كثب!

  • تتيح أنواع أنماط السلسلة أمانًا أقوى في وقت الترجمة لبعض المكتبات (دعني أعود إليك بشأن الانتشار ... يهرب بعيدًا )

    • مُنشئ RegExp ، وسلاسل سداسية / أبجدية رقمية ، وإعلانات مسار المسار ، ومعرفات السلسلة لقواعد البيانات ، وما إلى ذلك.


لماذا تكون regexes الخاصة بك سيئة للغاية؟

أرادت مجموعة من حالات الاستخدام التي أثارها آخرون تقديم أنواع أنماط السلسلة لتناسب المكتبات الموجودة ؛ ولا يبدو أنه مقنع لفريق TS.

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

لكن هذه حالة استخدام فعلية صالحة لأنواع نمط السلسلة!


أنواع أنماط السلاسل للتحقق من صحة المجموعات الفائقة لقيم السلاسل الصالحة

بالتأكيد ، السلسلة التي تبدأ بـ / ، لا تنتهي بـ / ، ولا تحتوي على / ، ولا تحتوي على : ، ستمرر " تعبير HTTP لمسار ". ولكن هذا يعني فقط أن مجموعة القيم التي تمرر هذا التعبير العادي هي مجموعة شاملة من مسارات HTTP الصالحة.

علاوة على ذلك ، لدينا محلل فعلي لمسار URL يتحقق من عدم استخدام ? وعدم استخدام # ، وتم تخطي بعض الأحرف ، وما إلى ذلك.

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

ليس غالبًا أن يستخدم المستخدم ? في مسارات HTTP الخاصة بهم ، لأن معظمهم يتمتعون بالخبرة الكافية لمعرفة أن ? هو بداية سلسلة استعلام.


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

يتضمن هذا الخيط مجموعة متنوعة من حالات الاستخدام ؛ كانت الأمثلة الملموسة أكثر ندرة. من المثير للقلق أن العديد من هذه الأمثلة لا يبدو أنها كاملة - فهم يستخدمون RegExp يرفض المدخلات الصالحة.

لذلك ، بالتأكيد ، فإن الكثير من regexes المقترحة ليست "كاملة".
لكن طالما أنهم لا يرفضون المدخلات الصالحة ، فلا بأس ، أليس كذلك؟

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

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


أنواع أنماط السلسلة والتقاطعات

على أي حال ، ستكون أنواع التقاطع على أنواع أنماط السلسلة مفيدة للغاية!

يمكن كتابة المثال .append() كـ ،

append (str : (
  //Must start with forward slash
  & PatternOf</^\//>
  //Must not end with forward slash
  & PatternOf</[^/]$/>
  //Must not have consecutive forward slashes anywhere
  & not PatternOf</\/\//>
  //Must not contain colon
  & PatternOf</^[^:]+$/>
)) : SomeReturnType;

يمكن أن يكون not PatternOf</\/\//> أيضًا ،
PatternOf</^((([/])(?!\3))|[^/])+$/> لكن هذا أكثر تعقيدًا

شكرًا لك AnyhowStep على المظاهرات المكثفة. أردت أن أنتقدك لأنك جعلتني أقرأ كثيرًا ، لكن اتضح أنه كان مفيدًا للغاية!

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

// Today
function createServer(id: string, comment: string) {
  if (id.match(/^[a-z]+-[0-9]+$/)) throw new Error("Server id does not match the format");
  // work
}

// Nicer
function createServer(id: PatternOf</^[a-z]+-[0-9]+$/>, comment: string) {
  // work immediately
}

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

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

من المحتمل أن يتم ذلك باستخدام ميزات لغة TS أكثر عمومية مثل Variadic Kinds # 5453 واكتب الاستدلال عند نشر الأنواع الحرفية للسلسلة.

التكهنات المستقبلية:

const x: ['a', 'b', 'c'] = [...'abc'] as const;

type T = [...'def']; // ['d', 'e', 'f'];
type Guard<T extends string> =
  [...T] extends [...'https://', ...any[]] ? Promise<any> : never;

declare function secureGET<
  T extends string
>(url: T): Guard<T>;

const x = secureGET('https://a.com');
x.then(...) // okay

const z = secureGET('http://z.com');
z.then(...); // error
type NaturalNumberString<T extends string> =
  [...T] extends ('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9')[] ? T : never;

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

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

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

وأداء معظم التعبيرات العادية القابلة للتمثيل في DFA ليس بهذا السوء ، ومن الممكن التحقق من هذه المجموعات الفرعية / الفائقة.

لا يزال بإمكانك الحصول على * الرغم من ذلك.

const str : PatternOf</ab+c/> | PatternOf</ac/>

TijmenW اقرأ اقتراحي عن كثب - هناك بعض المبررات الخفية هناك ، وبعض الميزات الصغيرة التي تجعله عمليًا في الواقع. لا يقتصر الأمر بشكل مباشر على تحديد القواعد النحوية الخالية من النجوم ، ولكن مجموعة شاملة صغيرة ممتدة بما يكفي فقط لجعلها مفيدة عمليًا لحالة الاستخدام شبه المتقدمة الخاصة بي. على وجه الخصوص ، يمكنك عمل starof ('a' | 'b' | ...) للأحرف الفردية ويمكنك استخدام string كمكافئ لـ starof UnionOfAllCodePoints (مما يجعلها في الواقع لم تعد بدائية من الناحية النظرية).

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

المهام: الرجاء مساعدتنا من خلال تحديد وظائف المكتبة الحقيقية التي يمكن أن تستفيد من أنواع RegExp والتعبير الفعلي الذي تستخدمه.

خذ هذا بحذر ، نظرًا لأنها مكتبة جديدة تمامًا ، ولكن أي مكتبة مثل https://github.com/ostrowr/ts-json-validator ستصبح أكثر فائدة مع شيء مثل نوع regex.

الهدف من المكتبة هو إنشاء أنواع أنواع / أزواج مخطط JSON <T, s> بحيث

  1. أي نوع يمكن لـ s التحقق من صحته يمكن تخصيصه لـ T
  2. أقل عدد ممكن من الأنواع القابلة للتخصيص إلى T فشل التحقق من الصحة عند تشغيله مقابل s .

من شأن نوع regex تحسين صرامة (2) من خلال السماح للنوع الذي تم التحقق من صحته بأن يكون أكثر صرامة بشأن الكلمات الرئيسية التالية على الأقل:

  • format
  • patternProperties
  • propertyNames

المهام: الرجاء مساعدتنا من خلال تحديد وظائف المكتبة الحقيقية التي يمكن أن تستفيد من أنواع RegExp والتعبير الفعلي الذي تستخدمه.

يمكن لجميع مكتبات واجهة Excel استخدام التحقق من النوع مثل A1 أو A5:B7 .

مفاتيح الملكية / مفهرسات سلسلة Regex

تعامل بعض المكتبات الكائنات وفقًا لأسماء الخصائص. على سبيل المثال ، في React نريد تطبيق أنواع على أي خاصية يبدأ اسمها بـ aria- :

interface IntrinsicElements {
    // ....
    [attributeName: /aria-\w+/]: number | string | boolean;
}

هذا مفهوم متعامد بشكل فعال (يمكننا إضافة أنواع Regex دون إضافة مفاتيح خصائص Regex ، والعكس صحيح).

أعلم أن هذا متعامد قليلاً مع كل ما يحدث هنا ولكن ويسلي يعتقد أنه يمكنك استخدام مدخلاتنا. يستمر هذا في الظهور في Fabric لعدة أسباب. كمكتبة مكونة ، نريد أن نكون قادرين على رفع واجهة مكونات المكون التي تعكس بدقة واجهة مكون React التي يسمح بها TypeScript ، بما في ذلك سمات data- و aria- . بدونها لا يمكننا رفع واجهات دقيقة لعملائنا لاستخدامها في هذه السمات. أصبحت هذه مشكلة أكبر مع الإصدار التالي من Fabric حيث نبحث في تطبيقات قابلة للتوصيل مثل الفتحات ونحتاج إلى تحديد هذه السمات والسماح بها على واجهات عشوائية.

إذا كان هناك أي شيء يمكننا القيام به للمساعدة ، فيرجى إبلاغي بذلك! 😄

ملعب TS :

import * as React from 'react';

// Want to reflect the same aria- and data- attributes here that JSX compiler allows in this interface:
interface TestComponentProps {
    someProp?: number;
}

const TestComponent: React.FunctionComponent<TestComponentProps> = () => {
    return null;
}

const ConsumerComponent: React.FunctionComponent = () => {
    // The React component interface allows for 'data-' and 'aria-' attributes, but we don't have any typesafe way of
    // elevating that interface or instantiating props objects that allow the same attributes. We just want to be able to 
    // define component interfaces that match what the React component interface allows without opening it up to 'any' and 
    // giving up all type safety on that interface.
    const testComponentProps: TestComponentProps = {
        someProp: 42,
        'data-attribute-allowed': 'test'
    };

    return (
        <TestComponent
            someProp={42}
            // 'data-' and 'aria-' attributes are only allowed here:
            data-attribute-allowed={'data-value'}
            aria-attribute-allowed={'aria-value'}
            {...testComponentProps}
        />
    )
}

المهام: الرجاء مساعدتنا من خلال تحديد وظائف المكتبة الحقيقية التي يمكن أن تستفيد من أنواع RegExp والتعبير الفعلي الذي تستخدمه

كرون الوظائف. (مندهش جدا لم يذكر هذا)

^((\*|\d+((\/|\-|,){0,1}(\d+))*)\s*){6}$

ما عليك سوى إضافة سنتي هنا - أنا أعمل في مشروع React حيث نرغب في التحقق من صحة خاصية سيتم استخدامها كسمة HTML id . هذا يعني أنه يجب أن يفي بالقواعد التالية وإلا سيحدث سلوك غير متوقع:

  1. لها شخصية واحدة على الأقل
  2. لا توجد مسافات

بعبارة أخرى:

interface Props {
  id: PatternOf</[^ ]+/>;
}

مثال آخر: معرّفات نوع الحرم مع السلاسل المتوقعة بالتنسيق '<namespace>/<name>[@<version>]'

حالة الاستخدام: واجهات برمجة تطبيقات DOM مكتوبة بسلسلة مثل Navigator.registerProtocolHandler() .

نقلا عن MDN:

لأسباب أمنية ، يقوم registerProtocolHandler() بتقييد المخططات التي يمكن تسجيلها.

قد يتم تسجيل مخطط مخصص طالما:

  • يبدأ اسم المخطط المخصص بـ web+
  • يتضمن اسم المخطط المخصص حرفًا واحدًا على الأقل بعد البادئة web+
  • يحتوي النظام المخصص على أحرف ASCII صغيرة فقط في اسمه.

بمعنى آخر ، يتوقع Navigator.registerProtocolHandler() إما string مشهورًا أو مخصصًا string ولكن فقط إذا كان يتوافق مع مخطط معين.

خصائص CSS المخصصة لـ CSSType هي حالة استخدام أخرى لتوفير أنواع مغلقة لجميع الخصائص باستثناء تلك المسبوقة بـ -- .

interface Properties {
    // ....
    [customProperty: /--[a-z][^\s]*/]: number | string;
}`

ذات الصلة https://github.com/frenic/csstype/issues/63

هل يمكن لأحد أن يخبرني ما إذا كان هذا هو نفس أنواع التصفية؟ https://github.com/microsoft/TypeScript/issues/7599

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

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

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

في هذا التعليق ،
https://github.com/microsoft/TypeScript/issues/6579#issuecomment -243338433

شخص ما مرتبط بـ ،
https://bora.uib.no/handle/1956/3956

بعنوان "مشكلة التضمين للتعبيرات العادية"


لكن،

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

https://www.sciencedirect.com/science/article/pii/S0022000011001486

(بالطبع ، تعبيرات JS العادية غير عادية)

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

أيضًا ، بشكل منفصل ، أرغب بشدة في الحصول على بديل أفضل لـ starof كمشغل لتصميم هذا النوع من الأشياء.

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

if (Gmail.test(candidate)) {
    // candidate is also an Email
}

nikeee Decidable لا يكفي ليكون هذا واقعيًا. حتى الوقت التربيعي يكون بشكل عام بطيئًا جدًا على هذا المقياس. ليس TS ، لكن لدي بعض المعلومات الأساسية حول قضايا مماثلة.

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

شكرا لتوضيح هذا!

حتى الوقت التربيعي يكون بشكل عام بطيئًا جدًا على هذا المقياس.

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

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

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

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

تحرير: أنا لا أريد أن أكرر هذا: أنا لست على فريق TS، فقط لتوضيح. لدي فقط خلفية جيدة في تصميم خوارزمية CS.

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

كيف يمكننا تصميم UX لدعم مجموعة فرعية فقط؟ قد لا يكون واضحًا جدًا للمستخدم أن الميزة X من RegEx تعمل ، لكن Y لا تعمل.

حسنًا ... لا تسميها "regex" - كبداية. ربما مجرد "مطابقة النمط" أو نحو ذلك: see_no_ evil :. لكن نعم ، على الأرجح ليست مهمة سهلة ...

ماذا عن بناء جملة غير regex مثل هذا:

type TLD = 'com' | 'net' | 'org';
type Domain = `${string}.${TLD}`;
type URL = `${'http'|'https'}://${Domain}`;

const good: URL = 'https://google.com'; // ✔️
const bad: URL = 'ftp://example.com'; // ✖️ TypeError: 'ftp' is not assignable to type 'http' | 'https'

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

type SubDomain = `${string}.`;
type Domain = `${SubDomain}?${string}.${TLD}`;

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

أعتقد أن هذا النهج سيكون أكثر سهولة في الاستخدام. ومع ذلك ، يبدو أن العمليات الحسابية على الأنواع تعتبر ذات كفاءة عالية.
وفقًا لـ https://github.com/microsoft/TypeScript/issues/15645#issuecomment -299917814 و https://github.com/microsoft/TypeScript/issues/15794#issuecomment -301170109 ، إنه قرار تصميم بعدم القيام بذلك علم الحساب على الأنواع.
إذا لم أكن مخطئًا ، فإن هذا النهج يمكن أن يخلق نوعًا هائلاً بسهولة. انصح:

type TLD = 'com' | 'net' | 'org' | 'ly' | 'a' | 'b' | 'c' | 'd';
type Foo = `${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}`;
type Bar = `${Foo}${Foo}${Foo}${Foo}${Foo}`

(هذا يفترض أن التطبيق سيستخدم أنواع الاتحاد. قد يعمل مع تطبيق مختلف / أكثر تعقيدًا)

إخلاء المسئولية: أنا لست جزءًا من فريق TS ولا أعمل على TS. فقط بلدي 2c.

nikeeerozzzly وهذا أكثر أو أقل جوهر اقتراحي ، فقط مع عدد قليل من السمات أصغر في عداد المفقودين. لقد استندت إلى مجموعة فرعية كبيرة من اللغات العادية (مفهوم اللغة الرسمية) ، وليس التعبيرات العادية بمعنى حرفي regexp وما إلى ذلك ، لذلك فهي أقل قوة بكثير من تلك ولكنها قوية بما يكفي لإنجاز المهمة.

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

يقول Math أن التحقق مما إذا كان النوع هو نوع فرعي من نوع آخر مكافئ حسابيًا للتحقق مما إذا كانت السلسلة مضمنة في لغة رسمية معينة.

يعد التحقق من صحة المجال على وجه التحديد أمرًا معقدًا جدًا يجب القيام به إذا قمت أيضًا بالتحقق من صلاحية TLD / اللاحقة العامة . المجالات العامة نفسها لكل RFC بسيطة مثل /[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)+/ + بحد أقصى 255 حرفًا ، ولكن حتى هذا الأمر معقد جدًا للكتابة ما لم تذهب لقواعد نحوية منتظمة كاملة كما يوضح التعبير العادي أعلاه. يمكنك إنشاء النوع برمجيًا بشكل واضح ومباشر ( سأتركه كتمرين للقارئ) باستخدام سلاسل فقط من

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

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

كانت آخر مرة قرأت فيها هذا الموضوع بأكمله منذ أكثر من عام. كنت في استراحة وشاهدت إشعارًا ، اقرأ تعليق rugk حول _ "حسنًا ... لا تسميها" regex "- لبدء التشغيل" _ الأمر الذي جعلني أفكر ... لم أكن أدرك أن أحدًا قد أدرك بالفعل قدم اقتراحًا أكثر تفصيلاً إلى حد كبير لنفس الفكرة _ (/ مشابهة جدًا) _.

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

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

ولكن كما تشيرnikeee وأنتما كلاكما ، يمكن أن يؤخذ هذا إلى أقصى درجات الخطورة. بافتراض تنفيذ أكثر سذاجة لدعم النقابات فقط. شخص ما سوف يفسد يوم الجميع بنشر تحديث لـ @types/some-popular-project يتضمن:

type MixedCaseAlphaNumeric = (
    | 'a'
    | 'b'
    | 'c'
    // and so on
);

type StrWithLengthBeteen1And64<Charset extends string> = (
    | `${Charset}`
    | `${Charset}|${Charset}`
    | `${Charset}|${Charset}|${Charset}`
    // and so on
);

function updatePassword(userID: number, password: StrWithLengthBetween1And64<MixedCaseAlphaNumeric>): void {
    // ...
}

بوضع ذلك في منظور ، يتكون هذا الاتحاد من أنواع مميزة وهي أكثر من الذرات في الكون المرئي .

الآن ، لقد رأيت بعض أخطاء التخصيص الطويلة بشكل مخيف ولكن تخيل الخطأ (غير المقطوع) لذلك ....

Type '"😢"' is not assignable to type '"a"|"b"|"c"..........."'.ts(2322)'

حسنًا .. هناك بعض المشكلات

rozzzly ما الذي يجعل هذا النوع مختلفًا (من حيث الجدوى) عن TupleWithLengthBeteen1And64<Charset> ؟
لا يتم إجبار برنامج التحويل البرمجي على توسيع كل نوع إلى نموذج عادي ، فإنه سينفجر بسرعة على الأنواع العادية إلى حد ما إذا حدث ذلك.
لا أقول أعتقد أن هذه المشكلة منطقية في الكتابة المطبوعة في الوقت الحالي ، حتى إذا كان "عدد صحيح بين 3 و 1024" (أعتقد أطوال تخصيص المخزن المؤقت للرسائل) يعتبر خارج النطاق.

simonbuchan على الأقل يجب أن توجد أنواع البادئة واللاحقة ، إذا لم يكن هناك شيء آخر. هذا بحد ذاته مطلوب للعديد من مكتبات وأطر DOM.

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

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

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


بافتراض استخدام جميع التعبيرات العادية التي يمكن تحويلها إلى DFA في regexp (concat ، union ، star ، intersection ، complete ، إلخ) ، فإن تحويل regexp إلى NFA هو O (n) ، والحصول على ناتج اثنين NFAs هي O (m * n) ، ثم عبور الرسم البياني الناتج لحالات القبول هو O (m * n). لذا ، فإن التحقق من المساواة اللغوية / المجموعة الفرعية لاثنين من regexps العادية هو أيضًا O (m * n).

المشكلة هي أن الأبجدية كبيرة هنا. تقتصر الكتب المدرسية على الحروف الأبجدية ذات الحجم 1-5 عادةً ، عند الحديث عن DFAs / NFA / التعبيرات العادية. ولكن مع JS regexps ، لدينا كل unicode كأبجدية لدينا. منحت ، يمكن أن تكون هناك طرق فعالة لتمثيل وظائف الانتقال باستخدام المصفوفات المتفرقة وغيرها من الاختراقات الذكية والتحسينات لاختبار المساواة / المجموعة الفرعية ...

أنا واثق من أنه من الممكن القيام بفحص النوع من المهام العادية إلى العادية بكفاءة إلى حد ما.

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

لقد عملت مؤخرًا في مشروع آلي صغير محدود ، لذا لا تزال المعلومات جديدة في ذهني = x

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

type TLD = 'com' | 'net' | 'org' | 'ly' | 'a' | 'b' | 'c' | 'd';
type Foo = `${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}`;
type Bar = `${Foo}${Foo}${Foo}${Foo}${Foo}`

(هذا يفترض أن التطبيق سيستخدم أنواع الاتحاد. قد يعمل مع تطبيق مختلف / أكثر تعقيدًا)

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

AnyhowStep JS backreferences هي الإنتاج الوحيد الحساس للسياق (وهو إنتاج بسيط ومحدود إلى حد ما - يمكن الإشارة إلى ما يصل إلى 9 مجموعات فقط بهذه الطريقة) ، وبقية قواعد regexp النحوية منتظمة ، ولهذا السبب أشك في ذلك يمكن تقريره. لكن بغض النظر ، أعتقد أننا يمكن أن نتفق على أنه ليس عمليًا بأي معنى للكلمة. 🙂

تحرير: الدقة

لقد أكدت هذا التعليق من rozzzly يعمل مع TS 4.1.0 كل ليلة!

type TLD = 'com' | 'net' | 'org';
type Domain = `${string}.${TLD}`;
type Url = `${'http'|'https'}://${Domain}`;

const success: Url = 'https://example.com';
const fail: Url = 'example.com';
const domain: Domain = 'example.com';

جربه في الملعب ولاحظ أن fail به خطأ في وقت التجميع 🤩


تحديث : بعد اللعب بهذه الميزة قليلاً ، لن تغطي العديد من حالات الاستخدام. على سبيل المثال ، لا يعمل مع سلسلة ألوان سداسية عشرية.

type HexChar = '0' | '1' | '2' | '3' | '4' | '5' | '6'| '7' | '8' | '9' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F';
type HexColor = `#${HexChar}${HexChar}${HexChar}${HexChar}${HexChar}${HexChar}`;
let color: HexColor = '#123456';

اليوم ، فشل ذلك مع "ينتج Expression نوعًا متحدًا معقدًا جدًا بحيث لا يمكن تمثيله. (2590)"

لقد أكدت هذا التعليق من rozzzly يعمل مع TS 4.1.0 كل ليلة!

type TLD = 'com' | 'net' | 'org';
type Domain = `${string}.${TLD}`;
type Url = `${'http'|'https'}://${Domain}`;

const success: Url = 'https://example.com';
const fail: Url = 'example.com';
const domain: Domain = 'example.com';

جربه في الملعب ولاحظ أن fail به خطأ في وقت التجميع 🤩

هذا من شأنه أن يحل مشكلة البيانات أو الأغنية التي يواجهها معظمنا في مكتبات UX إذا كان من الممكن تطبيقه على الفهارس.

هذا في الأساس ولكن من الواضح أن هذا لا يعمل لأن TS يسمح فقط بالسلسلة | عدد. بما أن هذا هو في الأساس سلسلة ، فهل يمكن تمكينه؟
https://www.typescriptlang.org/play؟target=99&ts=4.1.0-dev.20201001#code/LAKALgngDgpgBAEQIZicgzgCzgXjgAwBIBvAcgBMUkBaUgXxPTACcBLAOwHM78BuUDmBjMAZkgDG8AJIAVGEzjFQcFXADalVBkwAFZgHsoALmRakWALpGmbLvxB1QocfvYL0AV3GT06I3Fl5MFxFCipqISZSI1JIsHpeIA

_تحديث_: بعد اللعب بهذه الميزة قليلاً ، لن تغطي العديد من حالات الاستخدام. على سبيل المثال ، لا يعمل مع سلسلة ألوان سداسية عشرية.

type HexChar = '0' | '1' | '2' | '3' | '4' | '5' | '6'| '7' | '8' | '9' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F';
type HexColor = `#${HexChar}${HexChar}${HexChar}${HexChar}${HexChar}${HexChar}`;
let color: HexColor = '#123456';

اليوم ، فشل ذلك مع "ينتج Expression نوعًا متحدًا معقدًا جدًا بحيث لا يمكن تمثيله. (2590)"

كانت هناك بعض الإشارات إلى هذا القيد في ملاحظات الإصدار. يقوم بإنشاء قائمة بجميع التوليفات الصالحة الممكنة ، وفي هذه الحالة ستنشئ اتحادًا يضم 16777.216 (أي 16 ^ 6) عضوًا.

هذه فكرة رائعة ... قام Igmat بعمل بعض المنشورات الرائعة في عام 2016 والتي تبدو جيدة على الورق على أي حال.

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

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

لقد أكدت هذا التعليق من rozzzly يعمل مع TS 4.1.0 كل ليلة!

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

@ chadlavi-casebook

كانت هناك بعض الإشارات إلى هذا القيد في ملاحظات الإصدار. يقوم بإنشاء قائمة بجميع التوليفات الصالحة الممكنة ، وفي هذه الحالة ستنشئ اتحادًا يضم 16777.216 (أي 16 ^ 6) عضوًا.

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

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

أردت التأكد من أن مفاتيح الكائن الحرفي الذي تم تمريره إلى وظيفتي كانت أسماء فئات css صالحة

أنا واثق تمامًا من القول إن هذا غير ممكن مع التنفيذ الحالي. إذا كان هناك دعم للمحددات الكمية والنطاقات ، فمن المحتمل أن تحصل على التحقق من صحة أسماء فئات نمط BEM. التعبير القياسي js القياسي لذلك ليس فظيعًا:
^\.[a-z]([a-z0-9-]+)?(__([a-z0-9]+-?)+)?(--([a-z0-9]+-?)+){0,2}$
يمكنك أيضًا التخلص من المراسي لأنه مع استمرار التنفيذ ، فهي إما مطابقة من طرف إلى طرف أو لا شيء ، لذا فإن ^ و $ ضمنيًا. الآن هذا هو تعبير عادي بسيط نسبيًا لمجموعة فرعية ضيقة لما هو محدد css صالح. على سبيل المثال: ಠ_ಠ هو اسم فئة صالح. انا لا امزح.

أنا آسف. توجب علي فعل هذا.

لقد قمت بتطبيق لغات عادية في TypeScript.

  • ملعب رقم 1 ، عدد 1s قابل للقسمة على 3 ولكن ليس على 2

وبصورة أكثر دقة ، قمت بتنفيذ آلية محدودة حتمية بسيطة باستخدام TS 4.1

أعني ، يمكننا بالفعل تنفيذ آلات Turing في TS. لذلك ، تعد DFAs و PDA "سهلة" مقارنة بذلك.

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


الأنواع الأساسية في الواقع بسيطة وتتناسب مع <30 LOC ،

type Head<StrT extends string> = StrT extends `${infer HeadT}${string}` ? HeadT : never;

type Tail<StrT extends string> = StrT extends `${string}${infer TailT}` ? TailT : never;

interface Dfa {
    startState : string,
    acceptStates : string,
    transitions : Record<string, Record<string, string>>,
}

type AcceptsImpl<
    DfaT extends Dfa,
    StateT extends string,
    InputT extends string
> =
    InputT extends "" ?
    (StateT extends DfaT["acceptStates"] ? true : false) :
    AcceptsImpl<
        DfaT,
        DfaT["transitions"][StateT][Head<InputT>],
        Tail<InputT>
    >;

type Accepts<DfaT extends Dfa, InputT extends string> = AcceptsImpl<DfaT, DfaT["startState"], InputT>;

إنه تحديد الآلات الآلية وهذا هو الجزء الصعب.

لكنني متأكد تمامًا من أنه يمكن لأي شخص إنشاء regex لمولد TypeScript DFA ™ ...


أود أيضًا أن أوضح أن مثال "سلسلة سداسية عشرية بطول 6" يوضح أنه يمكنك جعل معلمات الوظيفة تقبل فقط السلاسل المطابقة للتعبير المعتاد باستخدام القرصنة القبيحة ،

declare function takesOnlyHex<StrT extends string> (
    hexString : Accepts<HexStringLen6, StrT> extends true ? StrT : {__err : `${StrT} is not a hex-string of length 6`}
) : void;

//OK
takesOnlyHex("DEADBE")

//Error: Argument of type 'string' is not assignable to parameter of type '{ __err: "DEADBEEF is not a hex-string of length 6"; }'.
takesOnlyHex("DEADBEEF")

//OK
takesOnlyHex("01A34B")

//Error: Argument of type 'string' is not assignable to parameter of type '{ __err: "01AZ4B is not a hex-string of length 6"; }'.
takesOnlyHex("01AZ4B")

هنا ملعب مكافأة ؛ يقوم بتنفيذ regex /^hello .*/

وملعب آخر. يقوم بتنفيذ regex / world$/

مثال أخير ، ساحة اللعب ؛ هذا هو التعبير العادي لسلسلة الفاصلة العائمة !

AnyhowStep حسنًا ، لقد استخدمت فكرة DFA لتنفيذ تعبير بسيط [abc]{4} وهو ما يعني الأحرف abc بأي ترتيب مع فقد ولكن بطول 4. (aaaa ، abcc ، bbcc ، إلخ ...).
ملعب

https://cyberzhg.github.io/toolbox/min_dfa؟regex=ZCgoYmQqYiopKmMpKg==

https://github.com/CyberZHG/toolbox

إذا كان لدي المزيد من الإرادة ، فسأحصل على شيء مثل ما سبق وأستخدمه لتحويل regexes إلى TS DFAs ™ lol

حسنًا ، لقد جمعت نموذجًا أوليًا معًا ،

https://glitch.com/~sassy-valiant-heath

[تحرير] https://glitch.com/~efficacious-valley-repair <- ينتج عن هذا إخراج أفضل بطريقة لتطبيقات regex أكثر تعقيدًا

[تحرير] يبدو أن Glitch سيقوم بأرشفة المشاريع المجانية غير النشطة لفترة طويلة جدًا. إذن ، إليك git repo بالملفات ،
https://github.com/AnyhowStep/efficacious-valley-repair/tree/main/app

الخطوة 1 ، أدخل regex هنا ،
image

الخطوة 2 ، انقر فوق تحويل ،
image

الخطوة 3 ، انقر فوق عنوان URL الخاص بملعب TS الذي تم إنشاؤه ،
image

الخطوة 4 ، قم بالتمرير لأسفل حتى InLanguage_0 ،
image

الخطوة 5 ، العب بقيم الإدخال ،
image

image

صرخ إلى kpdyer ، مؤلف https://www.npmjs.com/package/regex2dfa ، للقيام بالرفع الثقيل لعملية التحويل

في حال احتاج شخص ما إلى شيء أقوى قليلاً ، فإليك آلة تورينج 😆

ملعب

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

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