Typescript: الاقتراح: الأنواع المتغيرة - أعط أنواعًا محددة للوظائف المتنوعة

تم إنشاؤها على ٢٩ أكتوبر ٢٠١٥  ·  265تعليقات  ·  مصدر: microsoft/TypeScript

أنواع مختلفة

أعط أنواعًا معينة من الوظائف المتنوعة

يتيح هذا الاقتراح لـ Typescript إعطاء أنواع للوظائف ذات الترتيب الأعلى التي تأخذ عددًا متغيرًا من المعلمات.
تتضمن وظائف مثل هذه concat و apply و curry و compose وتقريباً أي مصمم يلف وظيفة.
في جافا سكريبت ، من المتوقع أن تقبل هذه الدوال ذات الترتيب الأعلى دوال متباينة كوسائط.
مع معايير ES2015 و ES2017 ، سيصبح هذا الاستخدام أكثر شيوعًا حيث يبدأ المبرمجون في استخدام وسيطات الانتشار ومعلمات الراحة لكل من المصفوفات والكائنات.
يعالج هذا الاقتراح حالات الاستخدام هذه باستراتيجية كتابة واحدة عامة جدًا تستند إلى الأنواع ذات الترتيب الأعلى.

سيعالج هذا الاقتراح بشكل كامل أو جزئي عدة قضايا ، بما في ذلك:

  1. # 5331 - مجموعات كأنواع للراحة ... الحجج
  2. # 4130 - يقوم المترجم بالإبلاغ بشكل غير صحيح عن عدم تطابق توقيع المعامل / الاستدعاء المستهدف عند استخدام عامل الانتشار
  3. # 4988 - يجب أن تكون المجموعات قابلة للاستنساخ باستخدام Array.prototype.slice ()
  4. # 1773 - الأدوية المتنوعة؟
  5. # 3870 - أنواع الراحة في الأدوية العامة لأنواع التقاطع.
  6. # 212 - الربط والاستدعاء والتطبيق غير مكتوب (يتطلب أنواع هذه الوظيفة # 3694).
  7. # 1024 - مكتوب ... بقية المعلمات مع الأدوية

سأقوم بتحديث هذا الاقتراح على مفترقتي الخاصة بكتيب Typescript: sandersn / TypeScript-Handbook @ 76f5a75868de3fb1ad4dbed5db437a8ab61a2698
لدي تنفيذ قيد التنفيذ في sandersn / TypeScript @ f3c327aef22f6251532309ba046874133c32f4c7 والذي تم حاليًا تنفيذ الأجزاء البسيطة من الاقتراح.
يحل محل الجزء 2 من اقتراحي السابق ، رقم 5296.
تحرير: تمت إضافة قسم حول التخصيص. لم أعد متأكدًا من أنه يتفوق بدقة على # 5296.

معاينة المثال مع curry

curry للوظائف ذات الوسيطتين سهلة الكتابة في Javascript و Typescript:

function curry(f, a) {
    return b => f(a, b);
}

وفي الكتابة المطبوعة مع التعليقات التوضيحية من النوع:

function curry<T, U, V>(f: (t: T, u: U) => V, a:T): (b:U) => V {
    return b => f(a, b);
}

ومع ذلك ، من السهل كتابة نسخة متغيرة في جافا سكريبت ولكن لا يمكن إعطاء نوع في TypeScript:

function curry(f, ...a) {
    return ...b => f(...a, ...b);
}

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

function curry<...T,...U,V>(f: (...ts: [...T, ...U]) => V, ...as:...T): (...bs:...U) => V {
    return ...b => f(...a, ...b);
}

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

لنلقِ الآن نظرة على مثال استدعاء لـ curry :

function f(n: number, m: number, s: string, c: string): [number, number, string, string] {
    return [n,m,s,c];
}
let [n,m,s,c] = curry(f, 1, 2)('foo', 'x');
let [n,m,s,c] = curry(f, 1, 2, 'foo', 'x')();

في المكالمة الأولى ،

V = [number, number, string, string]
...T = [number, number]
...U = [string, string]

في المكالمة الثانية ،

V = [number, number, string, string]
...T = [number, number, string, string]
...U = []

بناء الجملة

صيغة المتغير من النوع المتغير هي ...T حيث _T_ هو معرّف يكون حسب الاصطلاح حرفًا كبيرًا واحدًا ، أو T متبوعًا بمعرّف PascalCase .
يمكن استخدام متغيرات النوع المتغير في عدد من السياقات النحوية:

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

function f<...T,...U>() {}
}
class C<...T> {
}

ويمكن الرجوع إليها في أي نوع من مواقع التعليقات التوضيحية:

function makeTuple<...T>(ts:...T): ...T {
    return ts;
}
function f<...T,...U>(ts:...T): [...T,...U] {
    // note that U is constrained to [string,string] in this function
    let us: ...U = makeTuple('hello', 'world');
    return [...ts, ...us];
}

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

let t1: [...T,...U] = [...ts,...uProducer<...U>()];
let t2: [...T,string,string,...U,number] = [...ts,'foo','bar',...uProducer<...U>(),12];

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

function f<...T>(ts:...T): [...T,string,string] { 
    // note the type of `us` could have been inferred here
    let us: [string,string] = makeTuple('hello', 'world');
    return [...ts, ...us];
}

let tuple: [number, string] = [1,'foo'];
f<[number,string]>(tuple);

دلالات

متغير النوع المتغير يمثل نوع الصف بأي طول.
نظرًا لأنه يمثل مجموعة من الأنواع ، فإننا نستخدم مصطلح "النوع" للإشارة إليه ، بعد استخدامه في نظرية النوع.
نظرًا لأن مجموعة الأنواع التي تمثلها عبارة عن مجموعات من أي طول ، فإننا نؤهل "النوع" بـ "varadic".

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

function f<...T>(): ...T {
    let a: ...T;
}

سيؤدي استدعاء دالة ذات وسيطات مكتوبة كنوع متغير إلى تعيين نوع tuple محدد لهذا النوع:

f([1,2,"foo"]);

يعيّن نوع المجموعة ...T=[number,number,string] ... T . So in this application of f , let a: ... T is instantiated as let a: [number، number، string] . However, because the type of a is not known when the function is written, the elements of the tuple cannot be referenced in the body of the function. Only creating a new tuple from a` مسموح به.
على سبيل المثال ، يمكن إضافة عناصر جديدة إلى المجموعة:

function cons<H,...Tail>(head: H, tail: ...Tail): [H,...Tail] {
    return [head, ...tail];
}
let l: [number, string, string, boolean]; 
l = cons(1, cons("foo", ["baz", false]));

مثل متغيرات النوع ، يمكن عادة استنتاج متغيرات النوع المتغير.
ربما تم وضع تعليقات توضيحية على المكالمات إلى cons :

l = cons<number,[string,string,boolean]>(1, cons<string,[string,boolean]>("foo", ["baz", false]));

على سبيل المثال ، يجب أن يستنتج cons متغيرين ، النوع _H_ والنوع _... Tail_.
في المكالمة الأعمق ، cons("foo", ["baz", false]) و H=string و ...Tail=[string,boolean] .
في المكالمة الخارجية ، H=number و ...Tail=[string, string, boolean] .
يتم الحصول على الأنواع المخصصة لـ _... Tail_ عن طريق كتابة القائمة الحرفية على هيئة مجموعات - يمكن أيضًا استخدام متغيرات من نوع المجموعة:

let tail: [number, boolean] = ["baz", false];
let l = cons(1, cons("foo", tail));

بالإضافة إلى ذلك ، يمكن استنتاج متغيرات النوع المتغير عند ربطها بالأنواع:

function car<H,...Tail>(l: [H, ...Tail]): H {
    let [head, ...tail] = l;
    return head;
}
car([1, "foo", false]);

هنا ، يتم استنتاج نوع l كـ [number, string, boolean] .
ثم H=number و ...Tail=[string, boolean] .

حدود الاستدلال على الكتابة

لا يمكن استنتاج الأنواع المتسلسلة لأن المدقق لا يمكنه تخمين أين يجب أن تكون الحدود بين نوعين:

function twoKinds<...T,...U>(total: [...T,string,...U]) {
}
twoKinds("an", "ambiguous", "call", "to", "twoKinds")

لا يمكن للمدقق أن يقرر ما إذا كان سيتم تعيينه أم لا

  1. ...T = [string,string,string], ...U = [string]
  2. ...T = [string,string], ...U = [string,string]
  3. ...T = [string], ...U = [string,string,string]

بعض المكالمات الواضحة هي ضحية لهذا التقييد:

twoKinds(1, "unambiguous", 12); // but still needs an annotation!

الحل هو إضافة نوع التعليقات التوضيحية:

twoKinds<[string,string],[string,string]>("an", "ambiguous", "call", "to", "twoKinds");
twoKinds<[number],[number]>(1, "unambiguous", 12);

يمكن أن تظهر التبعيات التي لا يمكن التحقق منها بين وسيطات النوع وجسم الوظيفة ، كما في rotate :

function rotate(l:[...T, ...U], n: number): [...U, ...T] {
    let first: ...T = l.slice(0, n);
    let rest: ...U = l.slice(n);
    return [...rest, ...first];
}
rotate<[boolean, boolean, string], [string, number]>([true, true, 'none', 12', 'some'], 3);

يمكن كتابة هذه الوظيفة ، ولكن هناك تبعية بين n والمتغيرات النوع: n === ...T.length يجب أن يكون صحيحًا حتى يكون النوع صحيحًا.
لست متأكدًا مما إذا كان هذا هو الرمز الذي يجب السماح به بالفعل.

دلالات في الفصول والواجهات

الدلالات هي نفسها في الفصول الدراسية والواجهات.

TODO: ربما توجد بعض التجاعيد الخاصة بالفئة في الدلالات.

التخصيص بين المجموعات وقوائم المعلمات

يمكن استخدام أنواع Tuple لإعطاء نوع لاستراحة وسيطات الوظائف داخل نطاقها:

function apply<...T,U>(ap: (...args:...T) => U, args: ...T): U {
    return ap(...args);
}
function f(a: number, b: string) => string {
    return b + a;
}
apply(f, [1, 'foo']);

في هذا المثال ، يجب f: (a: number, b:string) => string قائمة المعلمات ...T .
نوع الصف الذي تم استنتاجه هو [number, string] ، مما يعني أنه يجب (a: number, b: string) => string لـ (...args: [number, string]) => string .

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

function g(a: number, ...b: [number, string]) {
    return a + b[0];
}
g(a, ...[12, 'foo']);

تم إنشاء أنواع Tuple للمعلمات الاختيارية والباقية

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

function curry<...T,...U,V>(cur: (...args:[...T,...U]) => V, ...ts:...T): (...us:...U) => V {
    return ...us => cur(...ts, ...us);
}
function h(a: number, b?:string): number {
}
let curried = curry(h, 12);
curried('foo'); // ok
curried(); // ok

هنا ...T=([number] | [number, string]) ، لذا curried: ...([number] | [number, string]) => number يمكن استدعاؤه كما تتوقع. لسوء الحظ ، لا تعمل هذه الاستراتيجية مع معايير الراحة. هذه تتحول إلى مصفوفات:

function i(a: number, b?: string, ...c: boolean[]): number {
}
let curried = curry(i, 12);
curried('foo', [true, false]);
curried([true, false]);

هنا ، curried: ...([string, boolean[]] | [boolean[]]) => number .
أعتقد أن هذا يمكن دعمه إذا كانت هناك حالة خاصة للوظائف ذات معلمة tuple rest ، حيث يكون العنصر الأخير في المجموعة عبارة عن مصفوفة.
في هذه الحالة ، سيسمح استدعاء الوظيفة لوسائط إضافية من النوع الصحيح لمطابقة المصفوفة.
ومع ذلك ، يبدو هذا معقدًا جدًا بحيث لا يستحق العناء.

امتدادات لأجزاء أخرى من مطبوعة

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

    أمثلة

معظم هذه الأمثلة ممكنة كوظائف وسيطة ثابتة في كتابات الطباعة الحالية ، ولكن مع هذا الاقتراح يمكن كتابتها على أنها متغيرة.
البعض ، مثل cons و concat ، يمكن كتابته لمصفوفات متجانسة في Typescript الحالي ولكن يمكن كتابتها الآن لـ tuple غير المتجانسة باستخدام أنواع tuple.
هذا يتبع ممارسة جافا سكريبت النموذجية عن كثب.

إرجاع نوع متسلسل

function cons<H,...T>(head: H, tail:...T): [H, ...T] {
    return [head, ...tail];
}
function concat<...T,...U>(first: ...T, ...second: ...U): [...T, ...U] {
    return [...first, ...second];
}
cons(1, ["foo", false]); // === [1, "foo", false]
concat(['a', true], 1, 'b'); // === ['a', true, 1, 'b']
concat(['a', true]); // === ['a', true, 1, 'b']

let start: [number,number] = [1,2]; // type annotation required here
cons(3, start); // == [3,1,2]

النوع المتسلسل كمعامل

function car<H,...T>(l: [H,...T]): H {
    let [head, ...tail] = l;
    return head;
}
function cdr<H,...T>(l: [H,...T]): ...T {
    let [head, ...tail] = l;
    return ...tail;
}

cdr(["foo", 1, 2]); // => [1,2]
car(["foo", 1, 2]); // => "foo"

وظائف متنوعة كالحجج

function apply<...T,U>(f: (...args:...T) => U, args: ...T): U {
    return f(...args);
}

function f(x: number, y: string) {
}
function g(x: number, y: string, z: string) {
}

apply(f, [1, 'foo']); // ok
apply(f, [1, 'foo', 'bar']); // too many arguments
apply(g, [1, 'foo', 'bar']); // ok
function curry<...T,...U,V>(f: (...args:[...T,...U]) => V, ...ts:...T): (...us: ...U) => V {
    return us => f(...ts, ...us);
}
let h: (...us: [string, string]) = curry(f, 1);
let i: (s: string, t: string) = curry(f, 2);
h('hello', 'world');
function compose<...T,U,V>(f: (u:U) => U, g: (ts:...T) => V): (args: ...T) => V {
    return ...args => f(g(...args));
}
function first(x: number, y: number): string {
}
function second(s: string) {
}
let j: (x: number, y: number) => void = compose(second, first);
j(1, 2);

TODO: هل يمكن إرجاع f ...U بدلاً من U ؟

مصممون

function logged<...T,U>(target, name, descriptor: { value: (...T) => U }) {
    let method = descriptor.value;
    descriptor.value = function (...args: ...T): U {
        console.log(args);
        method.apply(this, args);
    }
}

أسئلة مفتوحة

  1. هل قصة قابلية التخصيص tuple-to-parameter-list تصمد؟ إنها مهتزة بشكل خاص حول المعلمات الاختيارية والباقية.
  2. هل سيكون النوع المستنتج اتحادًا من المجموعات كما في حالة المعلمة الاختيارية؟ نظرًا لأن bind و call و apply هي طرق معرّفة في الوظيفة ، يجب ربط وسيطات النوع الخاصة بهم في وقت إنشاء الوظيفة بدلاً من موقع الاتصال bind (على سبيل المثال). لكن هذا يعني أن الوظائف ذات الأحمال الزائدة لا يمكنها أن تأخذ أو تعيد أنواعًا خاصة بحججها - يجب أن تكون اتحادًا لأنواع التحميل الزائد. بالإضافة إلى ذلك ، لا تحتوي الوظيفة على مُنشئ يحدد وسيطات النوع مباشرةً ، لذلك لا توجد طريقة فعلاً لتوفير الأنواع الصحيحة لـ bind et al. TODO: أضف مثالاً هنا. لاحظ أن هذه المشكلة ليست بالضرورة فريدة بالنسبة للوظائف المتنوعة.
  3. هل يجب أن تكون معاملات الراحة ذات غلاف خاص للاحتفاظ بصيغة الاتصال اللطيفة ، حتى عندما يتم إنشاؤها من نوع tuple؟ (في هذا الاقتراح ، يجب أن تمرر الدوال المكتوبة بواسطة نوع tuple المصفوفات إلى معلمات الراحة الخاصة بها ، ولا يمكن أن تحتوي على معلمات إضافية.)
Fix Available In Discussion Suggestion

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

تم إصلاح هذه المشكلة الآن بواسطة # 39094 ، المحدد لـ TS 4.0.

ال 265 كومينتر

+1 ، هذا مفيد حقًا للبرمجة الوظيفية في TypeScript! كيف سيعمل هذا مع الحجج الاختيارية أو الباقية؟ أكثر واقعية ، هل يمكن استخدام الدالة compose في الدوال ذات الوسيطات الباقية أو الوسائط الاختيارية؟

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

في الواقع ربما تعمل أنواع النقابات بشكل أفضل. شيء مثل

function f(a: string, b? number, ...c: boolean[]): number;
function id<T>(t: T): T;
let g = compose(f, id): (...ts: ([string] | [string, number] | [string, number, boolean[]]) => number

g("foo"); // ok
g("foo", 12); // ok
g("foo", 12, [true, false, true]); // ok

هذا لا يزال يكسر معايير الراحة ، رغم ذلك.

ahejlsberg ، كان لديك بعض الأفكار حول كيفية عمل أنواع tuple ، على ما أعتقد.

إذن: +1: على هذا. للحصول على معلومات ، هذا يتعلق بـ (ويفي) # 3870. لقد حاولنا تنفيذ نوع إنشاء API في TypeScript ولكن يتعين علينا التغلب على بعض القيود المذكورة في هذا الاقتراح. هذا من شأنه أن يحل بالتأكيد بعض تلك المشاكل!

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

function compose<T, ...U>(base: T, ...mixins: ...U): T&U {
    /* mixin magic */
}

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

لسوء الحظ ، لا يعالج هذا الاقتراح كما هو # 3870 أو نوع التركيب ، نظرًا لأن عامل التكوين الوحيد لأنواع tuple هو [T,...U] . يمكنك أيضًا كتابة هذا كـ T + ...U (وهو أكثر دلالة على ما يحدث للأنواع) ، لكن # 3870 ومكتبة تكوين النوع تحتاج إلى T & ...U . أعتقد أن هذا قد يكون ممكنًا ، لكني بحاجة إلى فهم أفكارJsonFreeman و jbondc من # 3870 أولاً. سأقوم بتوسيع الاقتراح إذا كان بإمكاني معرفة كيفية عمله.

ملاحظة: قررت استخدام بناء الجملة [...T, ...U] لأنه يبدو وكأنه صيغة توزيع القيمة المكافئة ، لكن T + ...U أكثر دلالة على ما يحدث مع الأنواع. إذا انتهى بنا الأمر إلى كلاهما ، فقد يكون العاملان اللذان يجب استخدامهما + و & .

كبير: +1: على هذا!

+1 رائع! سيسمح بالتعبير عن مثل هذه الأشياء بشكل أكثر تعبيراً وخفة الوزن.

يبدو أن وجهة نظري في # 3870 مشكلة هنا. على وجه التحديد ، أنا قلق بشأن استنتاج وسيطات النوع لمعلمات النوع المتغير.

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

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

function f<...T>(x: ...T, y: ...T): ...T { }
f(['hello', 0, true], [[], 'hello', { }]); // what is the type returned by f?

jbondc ، - تبدو فكرة جيدة. سأضع ذلك في الاعتبار ولكن لن أستكشفه هنا ، لأنني أعتقد أنه يجب علينا تقديم مشغلات نوع جديدة واحدة تلو الأخرى. ينشئ كل من & و + أنواعًا جديدة ، لكن & يُنشئ نوع تقاطع بينما ينشئ + نوعًا جديدًا من نوع tuple (وهذا هو السبب في أنني أفضل بناء الجملة [T,...U] بدلاً من T + ...U ، لأن [T,U] يقوم بهذا بالفعل للأنواع).

JsonFreeman l أعتقد أنه من

  1. اتحاد الأنواع: f(['hello', 1], [1, false]): [string | number, number | boolean]
  2. عدم السماح بالاستدلال على معاملات نوع الصف المتكرر ، خاصةً إذا كان استنتاج وسيطة النوع معقدًا. شيء من هذا القبيل:
f(['hello', 1], [1, false]) // error, type arguments required
f<[string, number]>(['hello', 1], [1, false]) // error, 'number' is not assignable to 'string'
f<[string | number, number | boolean]>(['hello', 1], [1, false]); // ok

أعتقد أن المكتبات الحقيقية (مثل الامتدادات التفاعلية Igorbek المرتبطة) ستحتوي عادةً على معلمة واحدة من نوع tuple ، لذلك على الرغم من أنه لا يمكن استخدام أي من (1) أو (2) بشكل خاص ، إلا أنه لا ينبغي أن يؤثر على رمز العالم الحقيقي كثيرًا.

في الأمثلة أعلاه ، curry هو الأصعب استنتاجًا - عليك تخطي f: (...args:[...T,...U]) => V ، استنتاج ...ts:...T ، ثم العودة وتعيين ...U إلى ما هو بقي بعد استهلاك ...T من معلمات f .

لقد بدأت في وضع النماذج الأولية لهذا (sandersn / TypeScript @ 1d5725d) ، لكن لم أحصل على هذا بعد. أي فكرة إذا كان هذا سيعمل؟

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

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

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

أعتقد أنه سيتعين عليك تخطي العديد من أنواع tuple التي تحدث في نفس السياق (على سبيل المثال ، المستوى الأعلى مثل (...T,string,...U) => V أو التسلسل مثل [...T,...U,...T] ). بعد ذلك ، يمكنك إجراء تمريرات متعددة على الأنواع التي تم تخطيها ، والقضاء على الأنواع التي تم استنتاجها بالفعل وإعادة تخطي الأنواع التي لا تزال غامضة. إذا لم يكن أي نوع واحد متاحًا للاستدلال في أي وقت ، فتوقف وأعد الخطأ.

إذن أجل. معقد.

قد تكون قادرًا على استلهام الأفكار من مشكلة مماثلة. إنها في الواقع تشبه إلى حد ما مشكلة الاستدلال على اتحاد أو تقاطع. عند الاستدلال على نوع الاتحاد الذي يتضمن معلمة نوع عضو في سياق الاستدلال ، كما هو الحال في function f<T>(x: T | string[]) ، فأنت لا تعرف ما إذا كنت تريد الاستدلال على T. قد يكون المظهر المقصود لنوع الاتحاد هو string[] . لذا فإن الكتابة المطبوعة تشير أولاً إلى جميع المكونات الأخرى ، ومن ثم إذا لم يتم إجراء أي استنتاجات ، فإنه يشير إلى T.

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

ماذا لو سمحت بنشر tuple فقط إذا كان هو النوع الأخير في تسلسله؟ لذلك ، سيتم السماح بـ [string, ...T] ، لكن [...T, string] لن يُسمح به؟

إذا فهمت بشكل صحيح ، فسيحل هذا في الواقع قصة mixin في TypeScript. هل أنا محق في هذا الفهم؟

يمكن. يمكنك ان تعطي مثالا؟ أنا لست بطلاقة مع أنماط mixin.

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

هل يمكننا ترك حالة معرّف معلمة النوع للمطور؟

@ aleksey-bykov +1. لا أرى سببًا لعدم حدوث ذلك.

المطورين الذين لديهم خلفية هاسكل سيقدرون ذلك.

آسف ، يمكن تحليل هذه الجملة بشكل غامض. قصدت "أو" التحليل بإحكام: "حسب الاصطلاح (حرف واحد كبير الحجم || T متبوعًا بمعرف PascalCase)". أنا لا أقترح تقييد حالة المعرفات ، فقط أشير إلى الاتفاقية.

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

آسف للخروج عن مساره. سؤالي الأخير المثير للفضول (إذا كنت لا تمانع في طرح السؤال) ما هو "اصطلاح" TypeScript الذي قد ينكسر ومن هو المعني؟

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

يجب كتابة هذا الاختيار ، بافتراض أن T & ...U يعني T & U & V & ... (وهو السلوك البديهي).

function assign<T, U, ...V>(obj: T, src: U, ...srcs: ...V): T & U & ...V {
  if (arguments.length < 2) return <T & U & ...V> obj

  for (const key of Object.keys(src)) {
    (<any> obj)[key] = (<any> src)[key]
  }

  if (arguments.length === 2) return <U> obj
  return mixin<T, ...V>(obj, ...srcs)
}

أو في ملف تعريف:

interface Object {
    assign<T, U, ...V>(host: T, arg: U, ...args: ...V): T & U & ...V
}

@ aleksey-bykov الاتفاقية التي أتحدث عنها هي حالة معرفات معلمات النوع. من هو المعني؟ الأشخاص الذين يتعين عليهم قراءة التعليمات البرمجية المنقولة الجديدة التي لم يسبق لهم رؤيتها من قبل - تساعد الاصطلاحات القراء الجدد على فهم التعليمات البرمجية الجديدة بشكل أسرع.

sandersn ما

function assign<a, b, ...cs>(x: a, y: b, ...zs: ...cs): a & b & ...cs;

isiahmeadows & و | العمليات على الأنواع ليست مغطاة في هذا الاقتراح ، على الرغم من أنني يجب أن أضيفها إلى الأسئلة المفتوحة / العمل المستقبلي إذا لم أقم بذلك. المشغل الوحيد المقترح حاليًا هو التسلسل: [THead, ...TTail] .

يتمثل أحد الاختلافات في أن التسلسل لا يزال ينتج نوعًا من نوع tuple بينما ينتج & و | أنواع التقاطع والوحدات على التوالي.

sandersn سيكون المثال الخاص بي assign في TypeScript تافهًا للتغيير مع ذلك.

بالرغم ان:

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

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

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

function foo<...T>(...rest: ...T): ...T { }
foo('str', 0, [0]);

هل النتيجة [string, number, number[]] ؟ قد يعني ذلك أنه يتعين عليك الاعتماد على استنتاج حجة النوع مع إضافة مرشحين بترتيب من اليسار إلى اليمين ، وهذا ليس افتراضًا تافهًا. ستكون أيضًا المرة الأولى التي يعرض فيها نظام الكتابة قائمة مرشحي الاستدلال للمستخدم.

أعلم أن هذا اقتراح تجريبي / مبكر ، ولكن يمكننا مناقشة بناء جملة ...T لمعلمات الراحة. من وجهة نظري ، هذا لا يعمل حقًا.
لذا فإن الصيغة المقترحة هي:

declare function f<...T>(...a: ...T);

دعنا نقارن مع البنية الحالية لمعلمات الراحة:

declare function f(...a: number[]);

لذا فإن نوع المعلمة a التي تلتقط وسيطات الباقي هي number[] ، لذلك يمكننا أن نفهم بوضوح أن هذه مصفوفة. عن طريق القياس ، يمكنني أن أستنتج أن ...T من الاقتراح يمثل مصفوفة أيضًا. لكن هذا ليس واضحًا جدًا.
بعد ذلك ، لنفترض أنه يمكننا تحديد المزيد من معاملات الراحة التقييدية:

declare function f(...a: [number, string]);
// same as
declare function f(c: number, d: string); // or very close to

حتى الآن ، ما زلنا نرى هذا النوع من a هو صفيف (وهو عبارة عن مصفوفة).

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

var a: [number, string] = [1, "1"];
var b = [true, ...a]; // this must be [boolean, number, string], but it doesn't work :)

لذا فإن ...a في حالة المتغير هو 1, "1" .

بناء الجملة الخاص بي لتحديد معلمات الراحة من خلال فكرة ...T :

declare function f<...T>(...a: [...T]);
declare function g<H, ...T>(head: H, ...tail: [...T]): [H, ...T];

بالنسبة لي هو أكثر منطقية.

Igorbek كنت أعمل على افتراض أن declare function f<...T>(...a: ...T); عملت بالفعل بهذا الشكل . لكني لا أرى فائدة كبيرة من declare function f(...a: [number, string]); .

لنكون أكثر وضوحا.

البنية المقترحة في الأصل لمعلمات الراحة:

function func<...T>(...a: ...T)

إذا كان بإمكاني فعل هذا

function g<...T>(...a: ...T): [number, ...T] { ... }

ثم سأكون قادرًا على القيام بذلك:

function f<...T>(...a: ...T): [...T] { return a; }

لذا فإن نوع a هو [...T] (نعود بذلك) ، لكننا حددناه على أنه ...T في التوقيع.
يمكننا القول أن ...T و [...T] متماثلان ، لكنها لا تعمل في حالة المتغيرات.
للمتغيرات:

var a = [1, 2];
[a] === [[1,2]];
[...a] === [1, 2];
f(...a) === f(1, 2)
...a === 1, 2 // virtually

إذا طبقنا نفس الشيء على معايير الراحة القياسية

function f(...a: number[]): number[] { return a; }

نوع a هو number[] (حسب نوع الإرجاع) ، تمامًا كما تم تعريفه في التوقيع.

isiahmeadows نعم ، function f(...a: [number, string]) لا يعمل. لقد طورت للتو أفكارًا حول كيفية تعاملنا مع معايير الراحة.

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

function f<...T, ...U>()
f<[number, string], [boolean, number]>();

يتحول الى:

f<...[number, string], ...[boolean, number]>();

لذلك قد ينجح هذا أيضًا:

function g<T1, T2, T3>()

g<A, B, C>();
// same as
g<...[A, B, C]>();
g<...[A], ...[B, C]>(); 
g<...[A], B, C, ...[]>();

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

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

بل وأفضل:

function<...T>(...a: T): T;
// same as
function<...T>(...a: [...T]): T;

أقترح إضافة بادئة [] إلى معرف النوع للدلالة على باقي معلمات النوع.

function fn<R, []T>(...a:[]T): R;

حرف واحد أقصر من ...T و (في رأيي) يصدر ضوضاء بصرية أقل.

@ aleksey-bykov أنا في الواقع من رأي مخالف في ذلك. لا يتناسب مع صيغة معلمة الراحة الحالية ، لذلك أعتقد أنه أيضًا أقل وضوحًا من لمحة.

[...T] / T كنوع معلمة مصفوفة الراحة يبدو أفضل بكثير بالنسبة لي. مرة أخرى ، قارن مع المصفوفة وعامل sprad الخاص بهم:

| صفائف | أنواع (من الاقتراح) | أنواع (التحديث الخاص بي) |
| --- | --- | --- |
| var x = [1,2] | لا | T = [T1, T2] |
| [0, ...x] === [0,1,2] | [T0, ...T] === [T0, T1, T2] | [T0, ...T] === [T0, T1, T2] |
| f(x) === f([1, 2]) | لا | f<T>() === f<[T1, T2]>() |
| f(...x) === f(1, 2) | f<...T>() === f<[T, T2]> ؟ | f<...T>() === f<T1, T2> |
| f(0, ...x) === f(1, 2) | f<T0, ...T>() === f<T0, [T, T2]> ؟ | f<T0, ...T>() === f<T0, T1, T2> |

من الاقتراح

function g<...T>(...x: ...T) {
 // being called as g(1, "a");
  var a: ...T; // [number, string] ?
  var b: [number, ...T]; // [number, number, string]
  var c: [...T]; // [number, string] - same as a ? so [...T] is same as ...T - weird
}

من التحديث الخاص بي

function g<...T>(...x: T) {
 // being called as g(1, "a");
  var a: T; // [number, string]
  var b: [number, ...T]; // [number, number, string]
  var c: [...T]; // [number, string]
}

التحديث يبدو أجمل الآن IMO. يبدو أن القوائم التي تمثل الأنواع تبدو لطيفة للغاية ، ولكن حتى Lisps المكتوبة لا تذهب إلى هذا الحد (أنواع homoiconic ، أي شخص؟: smile :).

أحصل على جاذبية النقاء ، لكنني أنظر أيضًا إلى الجانب العملي أيضًا. سيكون من السهل نسبيًا تنفيذ القوائم بمفردها ، ولكنها لا تتناسب مع بقية اللغة. إنها تقريبًا مثل المحاولات العديدة لتطبيق monads في Java (اللغة) أو lambdas في C - فهي دائمًا ما تكون قبيحة للغاية ومبتكرة.

sandersn يمكنني محاولة شرح ما أعنيه بكشف قائمة المرشحين. يُنشئ استنتاج وسيطة النوع قائمة بالمرشحين لكل معلمة نوع. ثم يتحقق مما إذا كان أي مرشح هو نوع فائق لجميع الآخرين ، وإذا كان الأمر كذلك ، فإن هذا المرشح هو الفائز. لذلك في المثال التالي:

function foo<T>(a: T, b: T): T {}
foo(["hi", 0], ["", ""]);

سيتم كتابة الوسيطات ، ثم استنتاجها لكل معلمة. سيتم إنشاء اثنين من المرشحين ، وهما (string | number)[] و string[] . لكن الأول سيفوز لأنه نوع فائق من الثاني. ونتيجة لذلك ، لم يلاحظ المستخدم أبدًا أن string[] كان موجودًا في الصورة على الإطلاق. يوجد استنتاج واحد لـ T ، وجميع المرشحين الآخرين غير مرئيين. هذا يعني أن هناك شيئين غير مرئيين للمستخدم ، وهما ترتيب المرشحين وتعدد المرشحين.

فيما يلي مشكلة في التعددية إذا كنت تعتمد على قائمة المرشحين كقائمة عناصر في المجموعة يُرمز إليها بـ ...T :

function foo<...T>(...rest: ...T): ...T
foo(0, 1);

أعتقد أنك قد ترغب في استنتاج [number, number] مقابل T نظرًا للقصد من اقتراحك كما أفهمه. ولكن بسبب احتواء السطر https://github.com/Microsoft/TypeScript/blob/master/src/compiler/checker.ts#L6256 ، ستتم إضافة المرشح number مرة واحدة فقط ، و T سيتم الاستدلال على [number] . هذه هي قضية التعددية التي كنت أتحدث عنها.

أما بالنسبة للترتيب ، فمن اليسار إلى اليمين. ولكن هناك العديد من الممرات ، وستتم إعادة معالجة الوسيطات إذا كانت تحتوي على تعبيرات وظيفية سيتم كتابتها وفقًا للسياق. إذا كانت هناك وسيطات n تحتوي على تعبيرات دالة مكتوبة سياقيًا ، فهناك n + 1 تمريرات فوق الوسيطات. مثال على ذلك Array.prototype.reduce ، حيث يتم كتابة معلمة initialValue بشكل فعال واستنتاجها قبل رد النداء ، على الرغم من أنها على اليمين. لذلك قد يمثل شيء من هذا القبيل مشكلة بالنسبة للاقتراح:

function foo<...T>(...rest: ...T): ...T
foo(x => x, 0);

حدسيًا ، يجب أن يكون T هو [(x: any) => any, number] ، ولكن إذا كنت تعتمد على الترتيب الذي تمت إضافة المرشح إليه ، فسيكون [number, (x: any) => any] . هذا لأن استدلال وسيطة النوع يتم تركه من اليسار إلى اليمين بشكل عام ، ولكن الوظائف الخاضعة للكتابة السياقية يتم تأجيلها حتى النهاية.

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

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

function foo<...T>(...rest: T) { ... }
foo(x => x, 0);
// to infer, the following function is used
function foo2<T0, T1>(rest0: T0, rest1: T1) { ... }
foo2(x => x, 0);
// inferred as
foo2<(x: any) => any, number>
// T0 = (x: any) => any
// T1 = number
// T = [T0, T1] = [(x: any) => any, number]

راجع للشغل ، هل يمكننا الاستدلال على أن يكون x => x من النوع { <T>(x: T): T; } ؟

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

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

بالنسبة إلى وجهة نظرك حول { <T>(x: T): T; } ، لا يتم تعميم ذلك بشكل جيد لكتابة أشياء مثل x => foo(x) حيث foo هي بعض الوظائف. ستحتاج إلى معرفة نوع x للقيام بتحليل التحميل الزائد لـ foo .

خطوة صغيرة للخروج من المعركة مع قواعد الاستدلال بمدقق الكتابة.
لدي تعليق / اقتراح حول بناء الجملة. أعتقد أن هناك خيارين متسقين لكن يستبعد أحدهما الآخر:

1. نوع الراحة الرسمية الحجج

إذا اخترنا هذا النموذج:

type F<...Args> = (...args:...Args) => ...Args

ثم يجب أن نستخدمه مثل

var a:  F // a: () => []
var b:  F<number> // b: (arg: number) => [number]
var c:  F<number, string> // c: (arg1: number, arg2: string) => [number, string]
...

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

2. الحجج الباقية من نوع Tuple

(...args:[string, number]) => boolean    IS EQUIVALENT TO   (s: string, n: number) => boolean

في هذه الحالة ، لدينا دائمًا عدد ثابت من الفتحات في قسم معلمات النوع الرسمي.

function f<T>(...args: T): T {
    return args;
}

نستنتج أن T يجب أن يكون نوع tuple إذا تم استيفاء أي من الشرطين:

  1. يستخدم T لمعلمات الراحة مثل (... args: T) => T.
  2. يستخدم T في تكوين انتشار مثل [... T] أو [number، ... T، string]

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

في هذه الحالة ، يمكننا أن نكتب أيضًا

function f<T>(...args: [...T]): [...T] {
    return args;
}

لكنها زائدة عن الحاجة.

أنا شخصياً أود أن أرى الإصدار الأحدث مطبقًا في TypeScript. JsonFreeman ،sandersn؟

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

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

بالنسبة للتوقيعات العامة ، يكون الأمر أكثر تعقيدًا بسبب استدلال وسيطة النوع. ماذا لو كان لديك ما يلي:

function callback(s: string, n: number): void { }
declare function foo<...T>(cb: (...cbArgs: T) => void, ...args: T): [...T];

foo(callback, "hello", 0, 1);

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

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

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

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

JsonFreeman في حالتك سيعيد foo [string, number, number] حيث سيتم استنتاج ذلك من ...args ، سيكون نوع cb المستنتج (string, number, number) => void وستتجاهل رد الاتصال الذي تم تمريره الوسيطة الأخيرة التي شائع جدًا في كل من TS و JS.

لا أحب فكرة استخدامهم نوعًا من أجهزة نظام الكتابة لتمثيل سلسلة مجردة من الأنواع

هذا بالضبط ما هم عليه ، JS لا يعرف عن tuples ، فقط TS. بالنسبة إلى TS ، فإن tuple هي سلسلة من الأنواع.

أنا أحب النهج القائم على tuple أيضًا. خاصة إذا كان بإمكاننا الحصول على توقيعات وظائف متوافقة مثل هذا:

// all are equivalent
(a: A, b: B, c: C) => R;
(a: A, b: B, ...rest: [C]) => R;
(a: A, ...rest: [B, C]) => R;
(...args: [A, B, C]) => R;

// this is more complicated 
(a: A, ...rest: T[]) => R;
(...args: [A, ...T]) => R; // no in current syntax

لا يمكننا التعبير عن الأخير باستخدام البنية الحالية ولكن يمكننا ذلك إذا اعتمدنا # 6229.
بالنسبة لي ، يبدو أن الطريقة الصحيحة هي استخدام المجموعات وتوحيد المجموعات للتعبير عن المزيد. بدون مجموعات tuple أكثر معبرة ، سيكون من الصعب أن يكون لديك شيء مثل [...T, ...T] لأن T مثل tuple لها طول مفتوح.

JsonFreeman على سبيل المثال الخاص بك ، أظهر Pajn تمامًا كما فهمت لذلك - لا توجد أي مشاكل مرئية في استنتاج هذه الأنواع.

JsonFreeman من الأفضل أن أستخدم هذا النحو

declare function foo<T>(cb: (...cbArgs: T) => void, ...args: T): T;
declare function foo<T>(cb: (...cbArgs: T) => void, ...args: T): [...T]; // same

حسنًا ، ربما قد يؤدي ذلك إلى بعض الغموض:

declare function foo<T>(...args: T): T;
foo(1); // T is [number] or number[]?

// however, here it'd be more explicit
declare function foo<T>(...args: T[]): T[];
foo(1); // T is number[]

// and here
declare function foo<T>(...args: [...T]): T;
foo(1); // T is [number]

يمكنني الحصول على فكرة نشر معلمة نوع الراحة في مجموعة. لكني لست متأكدًا من أنني أريد أن يتم تفسير معلمة نوع الراحة ضمنًا على أنها مجموعة. سيظل مثال

Igorbek أنت محق بشأن الغموض في number, string ، هناك نسختان محتملتان للتوقيع. وهي (arg1: number, arg2: string) => [number, string] بالإضافة إلى (arg1: [number, string]) => [number, string] (اعتماد التفسير الضمني للمجموعة من أجل المثال).

الشيء الغريب الآخر في تفسير tuple الضمني هو هذا: لنفترض أن لديك معامل نوع الراحة T يتم تمثيله على number, string . لنفترض الآن أنك قمت بتمريرها كوسائط من النوع ، Foo<T> . هل سيتم تفسير ذلك على أنه Foo<[number, string]> بينما Foo<...T> هو Foo<number, string> ؟ هناك حجة على ذلك ، حيث إنها ستمتد عامل الانتشار إلى نظام النوع. لكنني ما زلت أفضل تمثيل إصدار tuple كـ Foo<[...T]>

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

declare function foo<T>(...args: [...T]): void
foo<[number]>(1, 2)

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

// 1. unusual place
declare foo<T>(x: T, ...ys: [...T]): void

// 2. bad type
declare foo<T>(...xs: [...T]): void
foo<number>(2)

المثال الأول وثيق الصلة مباشرة بالوظيفة # تطبيق (ويمكن أن يكون ملف
خطأ) ، والثاني خطأ غير واضح سيفشل في التجميع ،
وغير تافهة للكشف عن طريق Intellisense.

في الأحد ، 28 فبراير 2016 ، 03:04 كتب جيسون فريمان [email protected] :

والشيء الغريب الآخر في تفسير الصف الضمني هو هذا: قل
لديك معلمة نوع الراحة T يتم إنشاء مثيل لها إلى رقم أو سلسلة.
لنفترض الآن أنك قمت بتمريرها كوسائط كتابة ، Foo. هل هذا ليكون
يتم تفسيره على أنه Foo <[number، string]> بينما Foo <... T> هو Foo سلسلة>؟

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/5453#issuecomment -189817561
.

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

المثال الثالث الخاص بك هو مشكلة أيضًا. بالنظر إلى تسلسل مثل number, string ، هناك نسختان محتملتان للتوقيع. وهي (arg1: number, arg2: string) => [number, string] بالإضافة إلى (arg1: [number, string]) => [number, string] (اعتماد التفسير الضمني للمجموعة من أجل المثال).

من المثال الثالث الواضح أنه لا يمكن تفسيره إلا على أنه (...args: [number, string]) => [number, string] :

declare function foo<T>(...args: [...T]): T;
foo(1, "a"); // T is [number, string]
const result: [number, string] = foo<[number, string]>(1, "a");

// however, it is assignable to/from the following signatures:
const f1: (arg1: number, arg2: string) => [number, string] = foo<[number, string]>;
const f2: (arg1: number, ...rest: [string]) => [number, string] = foo<[number, string]>;

الشيء الغريب الآخر في التفسير الضمني للبنية tuple هو: لنفترض أن لديك معلمة نوع الراحة T يتم تمثيلها على number, string .

لا يمكن إنشاء مثيل لـ T إلى number, string لأنه مجموعة حقيقية. يجب أن يكون [number, string] .

لنفترض الآن أنك قمت بتمريرها كوسائط من النوع ، Foo<T> . هل يتم تفسير ذلك على أنه Foo<[number, string]> بينما Foo<...T> هو Foo<number, string> ؟

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

هناك حجة على ذلك ، حيث إنها ستمتد عامل الانتشار إلى نظام النوع. لكنني ما زلت أفضل تمثيل إصدار tuple كـ Foo<[...T]>

هناك حالتان قد نستخدم فيهما بناء الجملة:

// in a signature declaration
declare function foo<[...T]>(...args: [...T]): [...T];
// and when type instantiated, so in the usage
type T = [number, string]
foo<T>();
foo<[...T]>();
// the latter can virtually be replaced as
type _T = [...T]; // which is a type operation that should produce [number, string]
foo<_T>();
// and more
type Extended = [boolean, ...T]; // [boolean, number, string]

لذلك ، بالنسبة للاستخدام ، فهو ليس أكثر من عامل تشغيل مثل | أو & أو [] . ولكن في الإعلان ، قد يتم تفسير بناء الجملة على أنه T extends any[] أو أي نوع أساسي لجميع المجموعات ، للإشارة إلى أنه يجب أن يكون من نوع tuple.

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

ماذا يحدث إذا حاولت نشر نوع tuple على عدد كبير جدًا
العوامل؟ مثله؟

declare function foo<T>(...args: [...T]): void
foo<[number]>(1, 2); // ok, foo<[number]> is of type (...args: [number]) => void
// [1, 2] is being passed in place of args
// is [1, 2] which is [number, number] assignable to [number]? yes, with current rules
// no error

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

// 1. unusual place
declare foo<T>(x: T, ...ys: [...T]): void
// 1. [...T] can be interpret as a type constraint "must be a tuple type"
// 2. if we call with type specified
foo<number>(1); // number doesn't meet constraint
foo<[number]>(1, 2); // argument of type 'number' is not assignable to parameter 'x' of type '[number]'
foo<[number]>([1], 2); // ok
// 3. if we call without type, it must be inferred
foo(1); // according to current rules, T would be inferred as '{}[]' - base type of all tuples
        // so, argument of type 'number' is not assignable to parameter 'x' of type '{}[]'
foo([1, 2], 2); // T is inferred as '[number, number]
                // rest arguments of type '[number]' are not assignable to rest parameters 'ys' of type '[number, string]'
foo([1], 2, 3); // T is '[number]',
                // x is of type '[number]',
                // ys is of type '[number]',
                // rest arguments are of type '[number, number]' which is assignable to '[number]',
                // no error

// 2. bad type
declare foo<T>(...xs: [...T]): void
foo<number>(2); // type 'number' doesn't meet constraint

ما زلت لا أرى فائدة تمثيل هذه الأشياء على أنها مجموعات. علاوة على ذلك ، أعتقد أنه يجب الإعلان عنها على أنها <...T> وليس <T> . كما قلت من قبل ، لا أرى أنواع tuple كجهاز مناسب لاستخدامه في تسلسل نوع الطول التعسفي في نظام الكتابة. ما زلت غير مقتنع بأن هذا مطلوب للتعبير الذي يريده الناس.

أوافق على أنه يمكن أن يكون أكثر تعبيرًا ، لكن وجود عامل "انتشار" في موضع معلمات النوع سيحدنا من اكتشاف حجج الراحة مرة واحدة فقط ، تمامًا كما لا يمكننا وضع معلمات الراحة مرتين. لذلك ، إذا أخذنا في الاعتبار <...T> و <A, B, C> ، فإن T سيلتقطهم كـ [A, B, C] . ولن نتمكن من التعبير عن <...T, ...U> لأنه سيكون غامضًا - [A, B, C], [] أو [A, B], [C] أو ... إلخ.

لنفترض أنني أردت التعبير عن دالة بالسلوك التالي:

declare function foo(a: A, b: B): R;
declare function boo(c: C, d: D, e: E): U;

let combined: (a: A, b: B, c: C, d: D, e: E) => [R, U] = combine(foo, boo);

// so the signature could be:

declare function combine<R, U, ???>(
  f1: (...args: [...T1]) => R,
  f2: (...args: [...T2]) => U):
    (...args: [...T1, ...T2]) => [R, U];

// if ??? is '...T1, ...T2'
combine<R, U, A, B, C, D, E> // what will be T1 and T2 ?
combine<R, U, ...[A, B, C], ...[D, E]> // ok ? so we will preserve spread to specific positions. so then
combine<...[R, U], A, ...[B, C, D], E> // will be restricted.
// however, ES6 allows to do it with function arguments
f(1, 2, 3);
f(...[1, 2], 3);
f(...[1], ...[2, 3]);

// if ??? is 'T1 extends TupleBase, T2 extends TupleBase'
// or just '[...T1], [...T2]' as a shortcut for such constraints
combine<R, U, [A, B, C], [D, E]> // pretty explicit, and doesn't occupy spread operator for type arguments

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

في جافا سكريبت ، يشبه الأمر function foo([...rest]) { } بدلاً من function foo(...rest) { } .

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

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

JsonFreeman السؤال: لماذا يجب أن يرضي [number] [1, 2] [number] ؟ هذا يبدو غريبا جدا بالنسبة لي هذا العمل في الواقع سيكون مفاجئًا للغاية. إنه ليس آمنًا على الإطلاق.

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

isiahmeadows بأي طريقة لا يمكن استبدال [1, 2] بـ [number] ؟ إنه بالتأكيد نوع فرعي. إنها نفس الطريقة التي يعتبر بها { x: 1, y: 2 } { x: number } صالحًا

تمام. سأتنازل جزئيًا ، لكن لا تأخذ في الاعتبار Function.prototype.apply ، والتي تقبل مجموعة من الوسائط.

interface Function<T, U, V> {
    (this: T...args: [...U]): V;
    apply(object: T, args: U): V;
}

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

أليس من النادر جدًا أن تقوم أي دالة JS برمي TypeError عند تمرير عدد كبير جدًا من الوسائط؟ ما هي بعض الأمثلة؟

isiahmeadows كمثال مجرد ، فهمت أن الخطأ الذي يقلقك هو:

function f(x: number): void {
  // throw if too many arguments
}
f.apply(undefined, [1,2,3]); // runtime error, no compile-time error
f(1,2,3) // compile-time error and runtime error.

هل هذا صحيح؟

sandersn ، أعتقد أن TypeError في العديد من الحجج هو شيء ينتهك روح JS ، حيث أننا عادة ما نمرر الوظيفة بحجج أقل رسمية من الحجج الفعلية التي سيتم تمريرها إلى هذه الوظيفة. نحن ببساطة لا نستخدمها. على سبيل المثال Array.prototype.forEach

ماذا عن وظيفة الكاري؟ ربما يكون هذا أكثر شيوعًا مع Ramda
ولوداش / fp.

في الإثنين ، 29 فبراير 2016 ، الساعة 13:45 كتب أناتولي ريسين [email protected] :

sandersn https://github.com/sandersn ، أعتقد أن TypeError قيد التشغيل أيضًا
العديد من الحجج شيء ينتهك روح JS ، مثلنا
عادةً ما تمرر الوظيفة بحجج أقل رسمية من الحجج الفعلية التي ستقوم بذلك
يتم تمريرها إلى هذه الوظيفة. نحن ببساطة لا نستخدمها. على سبيل المثال
Array.prototype.forEach

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/5453#issuecomment -190327066
.

isiahmeadows أود أن أقول إن arguments.length غير مستقر للغاية وعرضة للخطأ في وقت التشغيل. الكاري الحقيقي هو دليل إضافي على الجدل:

var plus = x => y => x + y
console.log(plus(3)(4)) // 7
console.log(plus(3,10)(4,20)) // still 7

عندما أقوم بتمرير وظيفتي بتوقيع ثابت كإعادة اتصال إلى مكان ما ، أفكر في الأمر بالطريقة التالية: "وظيفتي تتوقع _ على الأقل_ تلك الوسيطات"

ماذا عن أشياء مثل foldl ؟

const list = [1, 2, 3]
console.log(foldl((a, b) => a + b, 0, list))
console.log(foldl((a, b) => a + b, 0)(list))
console.log(foldl((a, b) => a + b)(0, list))
console.log(foldl((a, b) => a + b)(0)(list))

هذا شائع جدًا في البرمجة الوظيفية. وحذف الأخير
الحجة شائعة إلى حد ما.

في يوم الإثنين ، 29 فبراير 2016 ، الساعة 13:52 كتب أناتولي ريسين [email protected] :

isiahmeadows https://github.com/isiahmeadows سأقول هذا الكاري
على أساس aruments.length غير مستقر للغاية وعرضة للخطأ وقت التشغيل.
الكاري الحقيقي هو دليل إضافي على الجدل:

var plus = x => y => x + y
console.log (plus (3) (4)) // 7
console.log (plus (3،10) (4،20)) // لا يزال 7

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/5453#issuecomment -190330620
.

إذا كنت ترغب في تمرير ذلك باعتباره رد اتصال إلى ، على سبيل المثال ، map (العمل على قائمة
من القوائم) ، فربما تريد التخلص منها.

في الإثنين ، 29 فبراير 2016 ، 13:59 كتب Isiah Meadows [email protected] :

ماذا عن أشياء مثل foldl ؟

const list = [1, 2, 3]
console.log(foldl((a, b) => a + b, 0, list))
console.log(foldl((a, b) => a + b, 0)(list))
console.log(foldl((a, b) => a + b)(0, list))
console.log(foldl((a, b) => a + b)(0)(list))

هذا شائع جدًا في البرمجة الوظيفية. وحذف الأخير
الحجة شائعة إلى حد ما.

يوم الإثنين ، 29 فبراير 2016 ، الساعة 13:52 مساءً Anatoly Ressin [email protected]
كتب:

isiahmeadows https://github.com/isiahmeadows سأقول هذا الكاري
على أساس aruments.length غير مستقر للغاية وعرضة للخطأ وقت التشغيل.
الكاري الحقيقي هو دليل إضافي على الجدل:

var plus = x => y => x + y
console.log (plus (3) (4)) // 7
console.log (plus (3،10) (4،20)) // لا يزال 7

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/5453#issuecomment -190330620
.

أعتقد أن الأمر يتعلق في الغالب بما يلي:

type T = [number, string];
var a: T = [1, "a", 2]; // valid

// in this cases tuple types or parameter types cannot be inferred:
f(...a, true); // you could think number,string,boolean were passed, but weren't
const c = [...a, true]; // you could think that is of type [number, string, boolean] but it's not
// according to current rules, the best inferred types might be [number, string, number|string|boolean]

// same manner with variadic kinds, types are constructed properly:
type R = [...T, boolean]; // [number, string, boolean]

لهذا السبب اقترحت # 6229

السؤال عما إذا كان [1, 2] يرضي [number] هو سؤال صالح لطرحه ومناقشته. ولكن ما علاقته بميزة المجموعات القابلة للنشر؟

إنه ما إذا كان يجب أن يتجاهل التطبيق المتنوع لـ tuples الوسائط الإضافية
أم لا. هذه الوظيفة المثقلة بالأعباء يجب أن توضح المزيد من اهتماماتي.

declare function foo(x: number, ...args: string[]): void
declare function foo<T>(...args: [...T]): void
foo<[number]>(1, 2)

// This will always fail
declare function foo(x: number, ...args: string[]): void
declare function foo<T>(x: T): void
foo<number>(1, 2)

في الإثنين ، 29 فبراير 2016 ، الساعة 18:47 كتب جيسون فريمان [email protected] :

السؤال عما إذا كان [1 ، 2] يرضي [رقم] هو سؤال صحيح يجب طرحه
والنقاش. ولكن ما علاقته بميزة المجموعات القابلة للنشر؟

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/5453#issuecomment -190453352
.

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

في يوم الاثنين ، 29 فبراير 2016 ، الساعة 19:00 كتب Isiah Meadows [email protected] :

إنه ما إذا كان يجب أن يتجاهل التطبيق المتنوع لـ tuples العناصر الإضافية
الحجج أم لا. يجب أن توضح هذه الوظيفة المثقلة بالمزيد من تفاصيل ملفات
الاهتمام.

declare function foo(x: number, ...args: string[]): void


declare function foo<T>(...args: [...T]): void
foo<[number]>(1, 2)

// This will always fail
declare function foo(x: number, ...args: string[]): void
declare function foo<T>(x: T): void
foo<number>(1, 2)

في الاثنين 29 فبراير 2016 الساعة 18:47 جيسون فريمان [email protected]
كتب:

السؤال عما إذا كان [1 ، 2] يرضي [رقم] هو سؤال صحيح يجب طرحه
والنقاش. ولكن ما علاقته بميزة المجموعات القابلة للنشر؟

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/5453#issuecomment -190453352
.

JsonFreeman هذا بسبب عامل الانتشار للأنواع والمصفوفات / المجموعات. إذا كان عامل تشغيل نوع الانتشار مسموحًا به في شكل "الأنواع المعطاة A ، B و T = [A] ، فإن [...T, B] سيُنشئ [A, B] " (وهو مقترح ضمنيًا) فلن يتم محاذاته مع عامل مصفوفة / انتشار الصفيف. بالنظر إلى var a: [A] و var b: B ، لا يمكن إثبات أن نوع التعبير [...a, b] من النوع [A, B] . وفقًا لقواعد tuples الحالية ، يمكن إثبات أنها من النوع [A, A|B] .
هل هذا منطقي بالنسبة لك؟ أو يمكنني إنشاء جدول مقارنة لتسليط الضوء على عدم التطابق.

Igorbek أنا أفهم ما تقوله. ينبع هذا في النهاية من حقيقة أن المترجم لديه معرفة كاملة بالأنواع التي يتعامل معها ، ولكن ليس لديه معرفة كاملة بالقيم. على وجه الخصوص ، في المثال الخاص بك ، القيمة a لها طول غير معروف ، بينما النوع [A] يعرف الطول. كان هذا أحد الأسباب التي جعلتني غير مرتاح في البداية بشأن استخدام أنواع tuple لهذا الغرض. لكني لست متأكدًا من أنها مشكلة خطيرة.

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

سيكون الحل الآمن من النوع أكثر اتساقًا مع بقية
اللغة إذا كانت تحاكي بنية الوسيطة.

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

في الثلاثاء ، 1 آذار (مارس) 2016 ، الساعة 06:07 ، كتب Jason Freeman [email protected] :

Igorbek https://github.com/Igorbek أنا أفهم ما تقوله.
ينبع هذا في النهاية من حقيقة أن المترجم لديه معرفة كاملة
من الأنواع التي يتعامل معها ، ولكن ليس المعرفة الكاملة للقيم. في
بشكل خاص ، في مثالك ، القيمة a لها طول غير معروف ، بينما
النوع [A] له طول معروف. كان هذا أحد أسباب وجودي في البداية
غير مرتاح لاستخدام أنواع tuple لهذا الغرض. لكني لست متأكدا
إنها مشكلة خطيرة.

isiahmeadows https://github.com/isiahmeadows أرى ما تطلبه
حول ، ولكن لماذا تكون المشكلة أكثر وضوحًا مع معلمات نوع الراحة؟

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/5453#issuecomment -190667281
.

isiahmeadows هل يمكنك إعطاء مثال على كود لمشكلة الكاري؟

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

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

type FullCurry<T> = ((initial: T, xs: T[]) => T) | ((initial: T) => (xs: T[]) => T)
declare function foldl<T>(func: (acc: T, item: T) => T, initial: T, xs: T[]): T
declare function foldl<T>(func: (acc: T, item: T) => T): FullCurry<T>
declare function foldl<T>(func: (acc: T, item: T) => T, initial: T): (xs: T[]) => T

interface Function<T, R, ...A> {
    apply<U extends T>(inst: U, args: [...A]): R
    apply(inst: T, args: [...A]): R
}

function apply(reducer: (initial: number) => number): (number[]) => number {
    reducer.apply(undefined, [0, []])
}

const func = apply(foldl<number>((x, y) => x + y))

func([1, 2, 3]) // Runtime error

سأضيف الاختلاف الخاص بي أيضا. دعنا نرى مثال الكاري المتباين من الاقتراح:

function curry<...T,...U,V>(f: (...ts: [...T, ...U]) => V, ...as:...T): (...bs:...U) => V {
    return ...b => f(...as, ...b);
}

بعد ذلك ، بدأت في استخدامه:

function f(a: number, b: string, c: string) { return c.toUpperCase(); }
var a: [number, string] = [1, "boo", 2]; // valid
const cf = curry(f, ...a); // cf is of type string => string
cf("a"); // runtime error

isiahmeadows سواء تم تمثيلها كمعلمات لنوع الراحة أو

Igorbek أعتقد أن

JsonFreeman الأمر أكثر أنني أعترض على هذا السلوك:

class A {}
class B {}
class C {}

declare function foo(a: A, b: B): C;

// This should not work
let value: [A, B, C]
foo(...value)

هل هذا يوضح؟

isiahmeadows يجب أن تعمل في الواقع

تضمين التغريدة
أشعر أنه لا ينبغي. هذا أكبر اعتراض لي. أشعر أنه من المحتمل أن يكون خطيرًا إذا كان كذلك.

سؤال: ما هو نوع الإرجاع المستنتج ret ؟

declare function foo(a: A, b: B, c: C, d: D): D
let ret = foo.bind(...[new A(), new B(), new D()])

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

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

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

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

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

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

// I hate this idiom.
interface NestedArray<T> extends Array<Nested<T>> {}
type Nested<T> = T | NestedArray<T>

// I would much prefer this, but it requires non-strict type checking.
type Nested<T> = T | Nested<T>[]

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

ربما يكون هذا هو أكبر شيء يمنع الكتابة الصحيحة لـ Function.prototype.bind ، بخلاف حقيقة أنه سيتطلب توقيعًا معقدًا للغاية.

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

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

JsonFreeman هذا ما يقال ، هل تعتقد أنه كخطوة لتبني حجة نشر اقتراح المشغل # 6229 يجب أن يؤخذ في الاعتبار أولاً؟

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

وسيسمح الفحص غير الصارم للأنواع بالكسل الكافي لتسهيل حل هذه المشكلة بـ Function.prototype.bind . مع مثل هذا الكسل ، يمكنك إنجاز هذا النوع من خلال ما يلي (والذي سيتطلب بناء جملة tuple لتسلسلها ، ما لم تكن معلمات الراحة المتعددة على ما يرام في إعلان النوع):

interface Function {
    bind<R, T, ...X, ...Y>(
        this: (this: T, ...args: [...X, ...Y]) => R,
        thisObject: T,
        ...args: [...X]
    ): (this: any, ...rest: [...Y]) => R
}

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

// Values
declare function func(a: number, b: string, c: boolean, d?: symbol): number

let f = func.bind(null, 1, "foo")

// How to infer
bind<R, T, ...X, ...Y>(
    this: (this: T, ...args: [...X, ...Y]) => R,
    thisObject: T,
    ...args: [...X]
): (this: any, ...rest: [...Y]) => R

// Infer first type parameter
bind<number, T, ...X, ...Y>(
    this: (this: T, ...args: [...X, ...Y]) => number,
    thisObject: T,
    ...args: [...X]
): (this: any, ...rest: [...Y]) => number

// Infer second type parameter
bind<number, any, ...X, ...Y>(
    this: (this: any, ...args: [...X, ...Y]) => number,
    thisObject: any,
    ...args: [...X]
): (this: any, ...rest: [...Y]) => number

// Infer first part of rest parameter
bind<number, any, number, ...*X, ...Y>(
    this: (this: any, ...args: [number, ...*X, ...Y]) => number,
    thisObject: any,
    ...args: [number, ...*X]
): (this: any, ...rest: [...Y]) => number

// Infer second part of rest parameter
bind<number, any, number, string, ...*X, ...Y>(
    this: (this: any, ...args: [number, string, ...*X, ...Y]) => number,
    thisObject: any,
    ...args: [number, string, ...*X]
): (this: any, ...rest: [...Y]) => number

// First rest parameter ends: all ones that only uses it are fully spread
bind<number, any, number, string, ...Y>(
    this: (this: any, ...args: [number, string, ...Y]) => number,
    thisObject: any,
    ...args: [number, string]
): (this: any, ...rest: [...Y]) => number

// Infer first part of next rest parameter
bind<number, any, number, string, boolean, ...*Y>(
    this: (this: any, ...args: [number, string, boolean, ...*Y]) => number,
    thisObject: any,
    ...args: [number, string]
): (this: any, ...rest: [boolean, ...*Y]) => number

// Infer second part of next rest parameter
// Note that information about optional parameters are retained.
bind<number, any, number, string, boolean, symbol?, ...*Y>(
    this: (
        this: any,
        ...args: [number, string, boolean, symbol?, ...*Y]
    ) => number,
    thisObject: any,
    ...args: [number, string]
): (this: any, ...rest: [boolean, symbol?, ...*Y]) => number

// Second rest parameter ends: all ones that only uses it are exhausted
bind<number, any, number, string, boolean, symbol?>(
    this: (this: any, ...args: [number, string, boolean, symbol?]) => number,
    thisObject: any,
    ...args: [number, string]
): (this: any, ...rest: [boolean, symbol?]) => number

// All rest parameters that are tuples get converted to multiple regular
parameters
bind<number, any, number, string, boolean, symbol?>(
    this: (
        this: any,
        x0: number,
        x1: string,
        x2: boolean,
        x3?: symbol
    ) => number,
    thisObject: any,
    x0: number,
    x1: string
): (this: any, x0: boolean, x1?: symbol) => number

// And this checks

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

let f = func.bind(null, 1, Symbol("oops"))

// How to infer
bind<R, T, ...X, ...Y>(
    this: (this: T, ...args: [...X, ...Y]) => R,
    thisObject: T,
    ...args: [...X]
): (this: any, ...rest: [...Y]) => R

// Infer first type parameter
bind<number, T, ...X, ...Y>(
    this: (this: T, ...args: [...X, ...Y]) => number,
    thisObject: T,
    ...args: [...X]
): (this: any, ...rest: [...Y]) => number

// Infer second type parameter
bind<number, any, ...X, ...Y>(
    this: (this: any, ...args: [...X, ...Y]) => number,
    thisObject: any,
    ...args: [...X]
): (this: any, ...rest: [...Y]) => number

// Infer first part of rest parameter
bind<number, any, number, ...*X, ...Y>(
    this: (this: any, ...args: [number, ...*X, ...Y]) => number,
    thisObject: any,
    ...args: [number, ...*X]
): (this: any, ...rest: [...Y]) => number

// Infer second part of rest parameter
bind<number, any, number, string, ...*X, ...Y>(
    this: (this: any, ...args: [number, string, ...*X, ...Y]) => number,
    thisObject: any,
    ...args: [number, symbol /* expected string */, ...*X] // fail!
): (this: any, ...rest: [...Y]) => number

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


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

interface Function {
    apply<T, R, ...X>(
        this: (this: T, ...args: [...X]) => R,
        thisArg: T,
        args: [...X]
    ): R
}

ملاحظات قليلة أخرى:

  1. يجب أن تكون هناك طريقة لنشر المصفوفات والمجموعات كمعلمات لنوع الراحة.

ts interface Foo extends Function<void, ...string[]> {}

  1. يجب أن يكون هناك نوعان منفصلان للمُنشئين والمُستدعَين ، مع كون الدالات هي اتحاد الاثنين. يجب أن تنفذ الكائنات القابلة للاستدعاء الواجهة القابلة للاستدعاء ، ويجب على مُنشئي الفئة تنفيذ الواجهة القابلة للإنشاء ، ويجب أن تنفذ وظائف ES5 اتحاد الاثنين.
  2. Function.prototype.bind ويجب على الأصدقاء التحقق من كل الأحمال الزائدة للوظيفة. إذا كان هناك العديد من هذا العمل ، فيجب أن يعيد اتحادًا لهم جميعًا.

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

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

interface SomeType<T, R, ...X, ...Y> {
     someMethod(someArgs): [...X]; // No way of knowing how long X is 
}

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

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

_TL؛ DR: تخطي إلى فاصل الأسطر الأفقي. لدي فكرة جديدة أكثر عملية.

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

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

interface Function<R, T, ...A> {
    // Split it up for just this method, since it's being resolved relative to the
    // method itself.
    bind[...A = ...X, ...Y](
        this: (this: T, ...args: [...X, ...Y]) => R,
        thisObject: T,
        ...args: [...X]
    ): (this: any, ...rest: [...Y]) => R
}

هناك مشكلة أخرى المحتملة التي سوف يكون أصعب بكثير من العمل خارج (والسبب في أنني أعتقد أنه يجب أن تكون مقيدة إلى 2، لا _n _ أقسام):

declare function foo<...T>[...T = ...A, ...B, ...C](
    a: [...A, ...C],
    b: [...A, ...B],
    c: [...B, ...C]
): any

// This should obviously check, but it's non-trivial to figure that out.
let x = foo<
    boolean, number, // ...A
    string, symbol,  // ...B
    Object, any[]  // ...C
>(
    [true, 1, {}, []],
    [true, 1, "hi", Symbol()],
    ["hi", Symbol(), {}, []]
)

_عذرًا إذا كنت أتعمق في نظرية علوم الكمبيوتر هنا ..._

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

من المحتمل أن يكون هناك المزيد من المشكلات التي لم نفكر فيها.

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

التفكير في كيفية عمل نوع من الاقتراح مع بعض وظائف Bluebird.

interface PromiseConstructor {
    // all same type
    all<T>(promises: PromiseLike<T>[]):  Promise<T[]>;
    join<T>(...promises: PromiseLike<T>[]):  Promise<T[]>;
    // varying types
    all<...T>(promises: [...PromiseLike<T>]): Promise<[...T]>;
    join<...T>(...promises: [...PromiseLike<T>]): Promise<[...T]>;
    // this is sketchy...    ^
}

interface Promise<T> {
    // all same type
    then<U>(onFulfill: (values: T) => U): Promise<U>;
    spread<U>(onFulfill: (...values: T) => U): Promise<U>;
}
interface Promise<...T> {
    // varying types
    then<U>(onFulfill: (values: [...T]) => U): Promise<U>;
    spread<U>(onFulfill: (...values: [...T]) => U): Promise<U>;
}

هل لدينا حل لـ all<...T>(promises: [...PromiseLike<T>]): Promise<...T>; أعلاه؟

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

انظر تعليقي الكبير في PromiseConstructor. لقد قمت أيضًا بتصحيح واجهة Promise لتكون أقرب قليلاً إلى اقتراحي.

interface PromiseConstructor {
    new <T>(callback: (
        resolve:
        (thenableOrResult?: T | PromiseLike<T>) => void,
        reject: (error: any) => void
    ) => void): Promise<T, [T]>;
    new <...T>(callback: (
        resolve:
        (thenableOrResult?: [...T] | PromiseLike<[...T]>) => void,
        reject: (error: any) => void
    ) => void): Promise<[...T], ...T>;

    // all same type
    all<T>(promises: PromiseLike<T>[]):  Promise<T[], ...T[]>;
    join<T>(...promises: PromiseLike<T>[]):  Promise<T[], ...T[]>;

    // varying types
    all<...T>(promises: [...PromiseLike<T>]): Promise<[...T], ...T>;
    join<...T>(...promises: [...PromiseLike<T>]): Promise<[...T], ...T>;

    // all<...T>(promises: [...PromiseLike<T>]): Promise<[...T], ...T> should
    // expand to this:
    //
    // all<T1, T2, /* ... */>(promises: [
    //     PromiseLike<T1>,
    //     PromiseLike<T2>,
    //     /* ... */
    // ]): Promise<[T1, T2, /* ... */], T1, T2, /* ... */>;
    //
    // This should hold for all rest parameters, potentially expanding
    // exponentially like ...Promise<[Set<T>], ...Thenable<T>> which should
    // expand to something like this:
    //
    // Promise<[Set<T1>], Thenable<T1>, Thenable<T2> /* ... */>,
    // Promise<[Set<T2>], Thenable<T1>, Thenable<T2> /* ... */>,
    // // etc...
}

interface Promise<T, ...U> {
    // all same type
    then<V>(onFulfill: (values: T) => V): Promise<[V], V>;
    spread<V>(onFulfill: (...values: T) => V): Promise<[V], V>;

    // all same type, returns tuple
    then<...V>(onFulfill: (values: T) => [...V]): Promise<[...V], ...V>;
    spread<...V>(onFulfill: (...values: T) => [...V]): Promise<[...V], ...V>;

    // varying types
    then<V>(onFulfill: (values: [...U]) => V): Promise<[V], V>;
    spread<V>(onFulfill: (...values: [...U]) => V): Promise<[V], V>;

    // varying types, returns tuple
    then<...V>(onFulfill: (values: [...U]) => [...V]): Promise<[V], ...V>;
    spread<...V>(onFulfill: (...values: [...U]) => [...V]): Promise<[V], ...V>;
}

إذا تم توسيع [...Foo<T>] إلى [Foo<T1>, Foo<T2>, /*... Foo<TN>*/] ، فهل يعد [...Foo<T,U>] خطأ في بناء الجملة أم توسيعًا اندماجيًا؟

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

  1. إذا كانت واحدة بالضبط من T أو U هي معلمة راحة ، فإنها تتوسع بشكل طبيعي. بافتراض أن T معامل راحة ، فسيكون [Foo<T1, U>, Foo<T2, U>, /*... Foo<TN, U>*/] .
  2. إذا كان كلاهما معلمات راحة ، ويمكن استنتاج طولهما بشكل صحيح ، فيجب أن يكون توسيعًا اندماجيًا (حسنًا ... طول T مرات طول U).
  3. إذا لم تكن أي من معلمات الراحة ، فهذا خطأ في بناء الجملة.

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

interface Function<R, T, ...A> {
    // Split it up for just this method, since it's being resolved relative to the
    // method itself.
    bind[...A = ...X, ...Y](
        this: (this: T, ...args: [...X, ...Y]) => R,
        thisObject: T,
        ...args: [...X]
    ): (this: any, ...rest: [...Y]) => R
}

_ (إذا كان بإمكان شخص ما أن يأتي بتركيبة أفضل ، فأنا آذان صاغية. لا أحب ذلك ، لكن لا يمكنني التوصل إلى أي شيء لا يتعارض بصريًا.) _

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

مع 2. ، ما هو الترتيب الذي سيكون عليه التوسيع؟

[
Foo<T1, U1>, Foo<T2, U1>, /*... */ Foo<TN,U1>,
Foo<T1, U2>, Foo<T2, U2>, /*... */ Foo<TN,U2>,
/* ... */
Foo<T1, UN>, Foo<T2, UN>, /*... */ Foo<TN,UN>
]

أو بالعكس:

[
Foo<T1, U1>, Foo<T1, U2>, /*... */ Foo<T1,UN>,
Foo<T2, U1>, Foo<T2, U2>, /*... */ Foo<T2,UN>,
/* ... */
Foo<TN, U1>, Foo<TN, U2>, /*... */ Foo<TN,UN>
]

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


مجرد اقتراح بديل لتقسيم بناء الجملة:

interface Function<R, T, ...A> {
    bind<[...X, ...Y] = [...A]>(
        this: (this: T, ...args: [...X, ...Y]) => R,
        thisObject: T,
        ...args: [...X]
    ): (this: any, ...rest: [...Y]) => R
}

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

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

أنا أنظر إليها أيضًا أثناء توسيع الأول ، ثم الثاني لكل جزء من الجزء الأول. مثل هذا الرمز الزائف:

for (let TT of T) {
  for (let UU of U) {
    expand(TT, UU);
  }
}

تكرار بعض الأفكار أعلاه ...

interface Function<TReturn, TThis, ...TArgs> {
    bind<
        [...TBound, ...TUnbound] = [...TArgs],
        TNewThis
    >(
        thisObject: TNewThis,
        ...args: [...TBound]
    ): Function<TReturn, TNewThis, ...TUnbound>
}

هنا ، [...TBound, ...TUnbound] = [...TArgs] صالح لأن طول ...TBound معروف من طول args . كما يسمح بتغيير نوع TThis .

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

interface IFoo { a: number }
interface IBar extends IFoo { b: boolean }
function f(a: number) { }

let x = f.bind(<IBar>{ a: 1, b: false }, 2); // inferred type: Function<number, IBar>
let y = x.bind(<IFoo>{ a: 1 }) // inferred type: Function<number, IFoo>

النوع المستنتج من y غير صحيح ، يجب أن يكون Function<number, IBar> . لست متأكدًا مما إذا كان هذا مصدر قلق أم لا ، ولكن حله سيتطلب إدخال منطق في بناء الجملة <T> .

الخيار 1

interface Function<TReturn, TThis, ...TArgs> {
    bind<
        [...TBound, ...TUnbound] = [...TArgs],
        TNewThis = TThis is undefined ? TNewThis : TThis
    >(
        thisObject: TNewThis,
        ...args: [...TBound]
    ): Function<TReturn, TNewThis, ...TUnbound>;
}

الخيار 2

interface Function<TReturn, TThis, ...TArgs> {
    bind<
        [...TBound, ...TUnbound] = [...TArgs],
        TThis is undefined,
        TNewThis
    >(
        thisObject: TNewThis,
        ...args: [...TBound]
    ): Function<TReturn, TNewThis, ...TUnbound>;

    bind<
        [...TBound, ...TUnbound] = [...TArgs],
        TThis is defined
    >(
        thisObject: any,
        ...args: [...TBound]
    ): Function<TReturn, TThis, ...TUnbound>;
}

ومع ذلك من المحتمل أن يكون ذلك خارج نطاق هذا الاقتراح.

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

let a =        [1, 2];
let b = [0, ...a     , 3];
//      [0, ...[1, 2], 3]
//      [0,     1, 2 , 3]  // removed brackets

let c =               { a: 1, b: "b" };
let d = { e: true, ...c               , f: 3 };
//      { e: true, ...{ a: 1, b: "b" }, f: 3 };
//      { e: true,      a: 1, b: "b"  , f: 3 };

أنت تقترح تمديده لإنشاء مجموعة جديدة من الأنواع:

<...T> = <A, B, C>
...U<T> = <U<A>, U<B>, U<C>>

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

<...(from R in T select U<R>)> // linq-like
<...(T[R] -> U<R>)> // ugly

Igorbek ماذا عن استخدام عامل لتحديد ما سيتم توسيعه؟

interface PromiseConstructor {
    all<
      ...T, 
      [...TThen] = ...(PromiseLike<@T> | @T)
    >(
      promises: [...TThen]
    ): Promise<[...T], ...T>;
}

حيث يتوسع ...Foo<<strong i="9">@T</strong>, U> إلى [Foo<T1,U>, /*...*/, Foo<TN,U>] .

...(PromiseLike<@T> | @T) يتوسع إلى
[PromiseLike<T1>|T1, /*...*/, PromiseLike<TN>|TN]

بعض البدائل النحوية:

  • ...Foo<&T,U>
  • (T) Foo<T,U>
  • (...T => Foo<T,U>)
  • for (T of ...T) Foo<T,U>

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

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

بالتأكيد: +1: لحظر ذلك ، ربما لفترة طويلة. على الرغم من أنه من الجيد أن يكون لديك ، إلا أنه معقد جدًا للتنفيذ ، وسيكون اختراقًا كاملاً للقيام به ، نظرًا لأن TypeScript لا يستخدم نظام كتابة وظيفي آمن.

أتأخر قليلاً ، لكنني أتفق مع

يبدو أن حزم الأنواع في tuple متسقة مع استخدام Typescript لعامل الانتشار:

let [x, y, ...rest] = [1, 2, 3, 4, 5] // pack
foo(...params) // unpack
let all = [1, 2, ...other, 5] // unpack

// keep in mind this is already implemented, which kind of similar to mapping types
function map(arr) { ... }
let spreadingmap = [1, 2, ...map(other), 5];

مما يجعل التفكير في الأمر أسهل كثيرًا من <...T_values> = [T1, T2, T3, etc...] .

بينما تستخدم لغة ++ C عامل انتشار للتعبئة وعلامات القطع لتفريغ العبوة ، فإن استخدام السبريد لكليهما يكون أكثر اتساقًا مع Typescript.

module Promise {
  function all<...T_values>(   // pack into a tuple of types, conceptually identical to rest parameters
      values: [ (<PromiseLike<T*>> ...T_values) ]  // unpack, cast, then repack to tuple
  ): Promise<T_values> // keep it packed since T_values is a tuple of whatever types
}

isiahmeadowsJsonFreeman ما يمكن أن يكون الهدف من كل ذلك دون رسم الخرائط؟

أيضًا كما أثير في # 1336 ، ماذا عن المتغير Array.flatten ؟

jameskeane كان النصف الأول هو الفكرة الأولية ، لكنه لا يغطي حالة معلمة الراحة الوسطى (التي تمتلكها بعض واجهات برمجة التطبيقات):

function foo<...T>(a: Foo, b: Bar, ...rest: [...T, Baz]): Foo;

كما أنه لا يغطي Function.prototype.apply مقابل Function.prototype.call جيدًا.

أما # 1336 فيمكن تنفيذه بالمثل من خلال:

angular.module('app').controller(['$scope', function($scope: ng.IScope) { /*etc...*/ }]);

interface IModule {
  controller(injectable: [...string[], () => any]);
}

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

isiahmeadows في المثال أعلاه لحالة معلمة الراحة الوسطى ، ألا يتم حلها من خلال وجود مجموعات طول صارمة؟ أقرأ ...rest: [...T, Baz] مثل تفريغ السبريد arr = [...other, 123] . هذه هي نفس المشكلة التي أثرتها مع curry ، أليس كذلك؟

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

// as in
const t: [any, string] & [number, any] = [1, "foo"]

interface Function<R, T, ...A> {
    bind<...Y, ...Z>(
        this: (this: T, ...args: A & [...Y, ...Z]) => R, // tricky bit, luckily intersecting tuples is pretty easy
        thisObject: T,
        ...args: Y
    ): (this: any, ...rest: Z) => R
}

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

يفترض الاقتراح المتغير الحالي أن رقم 6229 انتهى به الأمر بالقبول (أي أن الصفوف صارمة بشكل افتراضي).

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

call و apply واضحان جدًا:

type Callable<R, T, ...A> = (this: T, ...args: [...A]) => R;

interface Function<R, T, ...A> {
    call(this: Callable<R, T, ...A>, thisArg: T, ...args: [...A]): R;
    apply(this: Callable<R, T, ...A>, thisArg: T, args: [...A]): R;
}

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

// Function.prototype.bind
type Callable<R, T, ...A> = (this: T, ...args: [...A]) => R;
type Constructible<R, ...A> = new (...args: [...A]) => R;

interface Function<R, T, ...A> {
    // my proposed syntax for splitting a rest parameter
    bind[[...A] = [...X, ...Y]](
        this: Callable<R, T, ...A>
        thisArg: T,
        ...args: [...X]
    ): Callable<R, any, ...Y>;

    bind[[...A] = [...X, ...Y]](
        this: Constructible<R, ...A>
        thisArg: T,
        ...args: [...X]
    ): Constructible<R, ...Y>;

    bind[[...A] = [...X, ...Y]](
        this: Callable<R, T, ...A> & Constructible<R, ...A>
        thisArg: T,
        ...args: [...X]
    ): Callable<R, T, ...Y> & Constructible<R, ...Y>;
}

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

interface Curried<R, T, ...XS> {
    // none passed
    (): this;

    // all passed
    (this: T, ...args: [...XS]): R;
}

interface CurriedMany<R, T, X, ...YS> extends Curried<R, T, X, ...YS>  {
    // penultimate case, constraint that ...YS contains no parameters
    [[...YS] = []](arg: X): Curried<R, T, X>;

    // otherwise, split rest into ...AS and ...BS, with `A` used as the pivot
    // (basically, default case)
    [[...YS] = [...AS, A, ...BS]](
        ...args: [X, ...AS]
    ): CurriedMany<R, T, A, ...BS>;
}

function curry<R, T>(f: (this: T) => R): (this: T) => R;
function curry<R, T, X>(f: (this: T, arg: X) => R): Curried<R, T, A>;
function curry<R, T, X, ...YS>(
    f: (this: T, arg: X, ...args: [...YS]) => R
): CurriedMany<R, T, X, ...YS>;

لا أعتقد أن الإضافات لـ curry ستجعلها Turing كاملة ، لكنها ستكون قريبة. أعتقد أن الشيء الأساسي الذي يمنعه هو القدرة على مطابقة تخصصات من نوع معين (وهي C ++ و Scala و Haskell ، وهي ثلاث لغات مع أنظمة كتابة Turing-Complete ، جميعها تمتلكها).

sandersn لم أتمكن من رؤية مثال أعلاه ، لكن هل يمكنني أن أسأل عن القيود المفروضة على المعلمات المتغيرة؟

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

interface HasKey<T> {
    Key(): T;
}

class Row<...T extends HasKey<X>, X> {
    // ...
}

_ بالمصادفة ، راجع https://github.com/Microsoft/TypeScript/issues/7848 للمناقشة حول احتمال إسقاط المتطلب الذي يجب أن يتم سرد X _

الآن هناك بعض الغموض المحتمل هنا حول ما إذا كان القيد:

  1. (...T) extends HasKey<X> أو
  2. ...(T extends HasKey<X>)

في هذا المثال أفترض 2.

هل ستكون هذه الأنواع من القيود (1 و / أو 2) ممكنة؟

من المحتمل أن يكون

حسنًا ... لقد أدركت للتو شيئًا ما: كيف يمكن أن تحتوي المصفوفات على أنواع متباينة؟ أو بشكل أكثر تحديدًا ، ما هو نوع arg أدناه؟

function processItems<...T>(...args: [...T]): void {
    for (const arg of args) { // Here
        process(arg);
    }
}

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

sandersn هل يمكنك التعليق على ما هي حالة هذه الميزة؟ أشعر أنه كان هناك الكثير من النقاش ، لكن لا يبدو أن هناك خطة محددة للميزة ، هل هذا صحيح؟

JsonFreeman كنت أسأل على وجه التحديد ما هو arg . في رأيي ، يجب أن يكون any للمثال الأصلي الخاص بي ، و Item<T> مع أدناه (مع T الذي يحده F):

function processItems<...T extends Item<T>>(...args: [...T]): void {
    for (const arg of args) { // Here
        process(arg);
    }
}

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

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

قصدت Item<any> في الثانية ... آسف.

عندما قلت أنه يجب أن يكون T ، كنت أفترض أن T هو نوع ، لكنني أعتقد أن بيت القصيد من هذه الميزة هو أن T ليس من النوع (على ما أعتقد). حسنًا ، أعتقد أنه يجب أن يكون any و Item<any> في الأمثلة الخاصة بك.

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

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

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

// To put it into code
function foo<...T>(list: [...T]): void {
    // This is allowed
    let xs: T[] = list

    // This is allowed
    let list2: [...T] = list

    // This is not allowed
    let list1: [...T] = xs

    // This is allowed
    let item: ?T = null

    // This is not allowed, since it's not immediately initialized
    let other: T

    for (let arg of args) {
        // This is allowed
        let alias: T = arg

        // This is allowed
        let other: ?T = arg

        // This is allowed, since `item` is defined upwards as `?T`
        item = arg

        // This is allowed, since you're doing an unsafe cast from `?T` to `T`.
        alias = item as T
    }
}

من المحتمل أن يكون هذا أكثر منطقية ، وسيكون أكثر مرونة.

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

إذا / عندما نلتزم بالدعم المناسب لراحة / انتشار الكائن (# 2103) ، فقد تكون الأنواع المتباينة قريبة بما يكفي لنشر الأنواع لتبرير القيام بها جميعًا مرة واحدة. (أنواع الحيزات هي نوع من أنواع الكائنات التي تبدو مثل { ...T, x: number, ...U, y: string, ...V } .)

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

sandersn هل bind و apply و call باستخدام this كتابة؟ أعتقد أن هذا سيكون بمثابة حل وسط مؤقت مقبول للكثيرين ، ويمكن أن يصاب ببعض الأخطاء في العملية لبعض المشاريع.

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

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

بدا لي أنه كان هناك اتفاق على أن T نوع من أنواع المتغيرات. في المثال الأصلي الخاص بك ، سيكون نوع arg هو نفسه نوع عنصر tuple (كما لاحظ JsonFreeman ، "نوع الاتحاد للعناصر"): تخيل الآن أن الكتابة المطبوعة تدعم استخدام tuple كبقية اكتب (# 5331).

function processItems<...T>(...args: T): void {
  for (const arg of args) { // Here - arg:number|string|boolean
    const other: ??? = arg; // I think the issue is, how to _represent_ this type?
  }
}
processItems(1, 'foo', false); // T is tuple [number, string, boolean]

بعيدًا عن هذا الاقتراح ، أعتقد أنه يجب أن تكون هناك طريقة لتمثيل "نوع العنصر" في المجموعة. قد يكون هذا استخدامًا آخر للسبريد ، على سبيل المثال فوق ...T :: number|string|boolean ؛ أن نشر نوع tuple ينتج عنه نوع العنصر الخاص به.

for (const arg of args) {
  const cst: ...T = arg;
}

// also, even without variadic types...
type Record = [number, string];
function foo(args: Record) {
  for (const arg in args) {
    const cst: ...Record = arg;
  }
}

مع وضع هذا في الاعتبار ، الأمثلة الأخرى الخاصة بك:

function foo<...T>(...list: T): void {
  let xs: T[] = [list, list] // array of the variadic tuple type

  // This is allowed
  let list5: (...T)[] = [...list]

  // This is *not* allowed
  let list2: [...T] = list

  // This is not allowed
  let list1: [...T] = xs

  // This **is** allowed
  // single element tuple, of variadic union
  // i.e. with number|string|boolean
  //      list4 = [1] or list4 = ['foo'] or list4 = [false]
  let list4: [...T] = [list[n]]

  // This **is**  allowed
  let other: T;

  // This is allowed
  let another: ...T;

  for (let arg of args) {
    another = arg; // allowed, if spreading the tuple is the union type

  }
}

لا أغيب عن هدفي الأصلي ، فقد أردت كتابة Promise.all ...

declare module Promise {
  function all<...T>(promises: Promise<...T>[]): T; // means promises is an array of promises to the union type, not what I wanted.

  // Then we need something like, which is now very confusing
  function all<...T>(promises: [...Promise<T*>]): T; 
}}

sandersn الآن بعد أن بدأت الميزات الأخرى المطلوبة في الاعتماد على هذا ، هل يمكن زيادة الأولوية؟ تعتمد الكتابة bind ، call إلخ. على هذا ، وبناء جملة ربط ES إذا / عندما يخرج يعتمد على ذلك ، لذلك هناك الآن المزيد من الركوب على هذا من مؤلفي المكتبة الملتويين الذين يزعجونك طوال الوقت . :)

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

هل يمكن استخدام هذا لتوفير تعريف نوع كامل لـ _.extend ، والذي يكون نوع الإرجاع الخاص به هو تقاطع جميع معلماته؟

declare module underscore {
  function extend<A, B, C, D, ...>(a: A, b: B, c: C, d: D, ...): A&B&C&D&...;
}

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

على الرغم من عدم تقديم أنواع متباينة كاملة ، فإن # 10727 جزء من الحل (ومن المرجح أن يتصدى للتحديات التي نواجهها (dojo)).

من الجيد سماع! على الرغم من أنها لا تزال في الواقع ليست أنواعًا متباينة. :( على سبيل المثال ، هذا الأسبوع عندما حاولت كتابة Object.assign ، وصلت إلى هذا الحد:

interface Object {
  // binary version
  assign<T,U>(target: T, source: U): { ...T, ...U };
  // variadic version: bind a variadic kind variable ...T
  // and then spread it using SIX dots
  assign<...T>(...targets: ...T): { ......T };
}

لاحظ أن بناء جملة "النقاط الست" هو انتشار كائن لمتغير نوع tuple ، والذي لم نناقشه بالفعل أعلاه.

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

مع Object.assign وجه الخصوص ، يمكن كتابته بهذه الطريقة ، والتقاط مجموعة فرعية تقنيًا (وإن كانت ضعيفة جدًا) ، نظرًا لأنها تغير هدفها (يجب أن يكون لديك نقطة مرجعية لهذا):

assign<T>(target: T, ...sources: Partial<T>[]): T;

الخلل في ذلك هو أنه يغير هدفه ، ويغير نوعه الهيكلي في مكانه.

isiahmeadows عندها T ليكون من النوع target بدون أنواع محاسبة sources . يمكنك تجربته الآن بإصدار غير متغير:

declare function _assign<T>(target: T, source: Partial<T>): T;
_assign({}, { a: 10 }); // T is {}

كما ذكرنا سابقًا ، يستخدم assign _ a نوع انتشار_ # 10727 ويمكن تعريفه بهذه الطريقة:

// non variadic
declare const assign: {
  <T>(target: T): T;
  <T, S>(target: T, source: S): {...T, ...S};
  <T, S1, S2>(target: T, source1: S1, source2: S2): {...T, ...S1, ...S2};
};
// variadic
declare function assign<T, [...S]>(target: T, ...sources: [...S]): {...T, ...[...S]};

_Note: ما زلت أصر على بناء الجملة المبني على tuple [...T] والذي يعتبر أكثر منطقية بالنسبة لي ._

sandersn راجع للشغل ، هل هناك أي تحديث يتعلق بموعد هبوط الأنواع المتغيرة؟ هل هناك أي فرصة لرؤيتها في 2.2؟
وفيما يتعلق بالصياغة ، هل ما زلت تقبل التعليقات على النحو أم أنك توافق على ذلك؟

لا يوجد إجماع واضح بين بناء الجملة ودلالات المستوى المنخفض حتى الآن.

في الثلاثاء ، 13 ديسمبر 2016 ، 13:26 كتب Igor Oleinikov [email protected] :

sandersn https://github.com/sandersn BTW ، أي تحديث عند
الأنواع المتغيرة سيتم هبوطها؟ هل هناك أي فرصة لرؤيتها في 2.2؟
وفيما يتعلق بالصياغة ، هل ما زلت تقبل التعليقات على النحو أو
هل توافقون على ذلك؟

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/5453#issuecomment-266819647 ،
أو كتم الخيط
https://github.com/notifications/unsubscribe-auth/AERrBIa5fE8PSk-33w3ToFqHD9MCFoRWks5rHuM5gaJpZM4GYYfH
.

أي فكرة عن وضع هذه القضية؟

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

سيكون بسيطًا هو إضافة نوع فائق Tuple extends any[] لا يمكن توصيفه إلا من خلال أنواع tuple. نظرًا لأن الفروق يجب أن تكون من الأنواع الفرعية any[] ، فإن هذا سيعمل:

declare interface Plugin<A: Tuple, P> {
  (...args: A): P | Promise<P>
}

const p: Plugin<[string, { verbose: boolean }], int> =
  (dest, { verbose = false }) => 4

في الوقت الحالي ، يُسمح باستخدام ...args: T[] فقط في نهاية التوقيعات.

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

/**
 * Takes a function with callback and transforms it into one returning a promise
 * f(...args, cb: (err, ...data) => void) => void
 * becomes
 * g(...args) => Promise<[...data]>
 */
function promisify<A extends Tuple, D extends Tuple, E>
    (wrapped: (...args: A, cb: (error: E, ...data: D) => void) => void)
    : ((...args: A) => Promise<Data>) {
  return (...args) => new Promise((resolve, reject) =>
    wrapped(...args, (e, ...data) =>
      e ? reject(e) : resolve(data)))
}

const write: ((fd: number, string: string, position?: number, encoding?: string)
              => Promise<[number, string]>) =
  promisify(fs.write)

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

function portable(func) {
    return function(...args) {
        if (this === undefined) {
            return func(...args)
        } else {
            return func(this, ...args)
        }
    }
}

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

function _flatMap<T, R>(
    array: T[],
    iteratee: (item: T) => R[]
): R[] {
    let result: R[] = []
    for (const item of array) {
        for (const value of iteratee(item)) {
            result.push(value)
        }
    }
    return result
}

const flatMap = portable(_flatMap)
Array.prototype.flatMap = flatMap

flatMap([1,2,3,4], x => [x, x])
// Is the same as
[1,2,3,4].flatMap(x => [x, x])
// Is the same as
flatMap.apply([1,2,3,4], [x => [x, x]])
// Is the same as
flatMap.call([1,2,3,4], x => [x, x])

نأمل الآن أن يكون من الواضح أن نوع flatMap (وليس _flatMap ) هو:

function flatMap<T, R>(this: T[], iteratee: (item: T) => R[]): R[]
function flatMap<T, R>(this: undefined, array: T[], iteratee: (item: T) => R[]): R[]

ومع ذلك ، ليس لدي طريقة لإضافة types للمحمول لأنني لا أستطيع استخراج نوع المعلمات من _flatMap لاستخدامها بعد ذلك ضمن تعريف النوع للوظيفة المزخرفة ، أتخيل مع هذا الاقتراح يمكنني اكتب شيئًا مثل:

// First argument to func is required for portable to even make sense
function portable<T, R, ...Params>(func: (first: T, ...rest: Params) => R) {
    // The arguments of calling with this is undefined should be simply
    // exactly the same as the input function
    function result(this: undefined, first: T, ...rest: Params): R
    // However when this is of the type of the first argument then the type
    // should be that the parameters are simply the type of the remaining
    // arguments
    function result(this: T, ...rest: Params): R
    function result(...args) {
        if (this === undefined) {
            return func(...args)
        } else {
            return func(this, ...args)
        }
    }
    return result
}

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

sandersn :

لديها حل لائق - ن زائدة

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

وفي الواقع ، شعر الكثيرون في هذا الموضوع باليأس الكافي للتعامل مع وظائفهم الخاصة لاقتراح المزيد من البنية التي ليست في الأصل جزءًا من هذا الاقتراح ، بما في ذلك ...... ، بالإضافة إلى ...*X ، [...T = ...A, ...B, ...C] و [...PromiseLike<T>] و <[...X, ...Y] = [...A]> و <PromiseLike<T*>> .

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

ملاحظة جانبية: بالنسبة لـ Ramda R.path قمنا بإنشاء كتابة من ألف سطر من الأحمال الزائدة ، والتي لا تزال تفتقد إلى دعم tuple (كانت التباديل قد تنفجر بطريقة أكثر صعوبة) ، وتسبب فقط في عدم إنهاء التجميع في المشاريع الحقيقية أي أكثر من ذلك. تم اكتشاف التكرار مؤخرًا كبديل قابل للتطبيق على ما يبدو هناك (# 12290).

بالمناسبة ، لم تقم بعد بالتعليق على الاقتراح المقدم من Artazor وIgorbek. ما رأيك في ذلك؟

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

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

v ... مقابل | تعريف (التقاط) | استخدام (انتشار)
- | - | -
وظيفة | type Fn = (...args: any[]) => {} | type Returns = typeof fn(...MyTuple); (# 6606)
مجموعة | إتلاف tuple-level. يمكن محاكاته تقنيًا باستخدام الوصول إلى الفهرس + الانتشار (انظر يمينًا) + العودية. | type Arr = [Head, ...Tail];
كائن | تدمير كائن على مستوى النوع. ليس ضروريًا ، فقط استخدم Omit ، انظر # 12215. | type Obj = { a: a, ...restObj }; (ليس ضروريًا ، مثل Overwrite ، انظر # 12215)
الأدوية | حدد type Foo<...T> للقيام بـ Foo<1, 2, 3> (يلتقط [1, 2, 3 ] في T ). مرح ، لكن لا تعرف ما هي حالة الاستخدام التي تتطلب ذلك. | حدد type Bar<A,B,C> للقيام بـ Bar<...[1,2,3]> ( A = 1 إلخ). كما سبق ، لا أعرف حالات الاستخدام التي تحتاج إلى ذلك.
النقابات (مكافأة) | ؟ | type Union = "a" | "b"; type MyTuple = ...Union; // ["a", "b"] (الطلب غير موثوق به ولكنه يتيح تكرار النقابات / العناصر من خلال المجموعات. على أي حال ، خارج النطاق هنا.)

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

declare function f<U, T>(head: U, ...tail: T): [U, ...T];

في سياق # 6606 ، هناك نوع آخر ذو صلة: القدرة على فك ضغط نوع tuple لتطبيق الوظيفة ، على سبيل المثال typeof f(...MyTuple) . أعتقد أن هذه كافية لحل المشكلات الأكثر صعوبة التي سمعت عنها هنا. لمحاولة تقديم بعض الحلول هنا:

jameskeane :

أعتقد أنه يجب أن تكون هناك طريقة لتمثيل "نوع العنصر" في المجموعة

إذا كنت ترغب في الحصول على اتحاد عناصرها ، فراجع TupleToUnion .

Promise.all

// helpers: `mapTuple` needs #5453 to define, #6606 to use
type TupleHasIndex<Arr extends any[], I extends number> = ({[K in keyof Arr]: '1' } & Array<'0'>)[I];
type Inc = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // longer version in gist
declare function mapTuple<F extends (v: T) => any, Tpl extends T[], T>(f: F, tpl: Tpl): MapFn<F, Tpl, T>;
type MapFn<
    F extends (v: T) => any,
    Tpl extends T[],
    T,
    // if empty tuple allowed:
    // I extends number = 0,
    // Acc = []
    // otherwise:
    I extends number = 1,
    Acc = [F(Tpl[0])]
> = { 1: MapFn<F, Tpl, T, Inc[I], [...Acc, F(Tpl[I])]>; 0: Acc; }[TupleHasIndex<Tpl, Int>];

declare module Promise {
  function all<Promises extends Promise<any>[]>(promises: Promises): typeof mapTuple(<T>(prom: Promise<T>) => T, Promises);
}

@ danvk :

_.extend

sandersn :

Object.assign

كلاهما مجرد نسخ متغيرة من Ramda mergeAll . لا حاجة لست نقاط!

isiahmeadows :

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

إذا فهمت بشكل صحيح ، فأنت قلق بشكل أساسي بشأن ما إذا كان النهج الذي يقدمه عدد قليل من الآخرين سيأخذ في الاعتبار معالجة الكتابة الأكثر صرامة مثل curry و bind التي ذكرتها. هذا هو رأيي في ذلك بالذات ، بعد اقتراحهم.
الإستراتيجية متشابهة بعض الشيء ، تتحايل حول حقيقة أنه من الصعب القول ، استخرج متطلبات النوع للمعلمات i ~ j من نوع دالة إلى نوع tuple ، عن طريق تأجيل التحقق من نوع الوسيطات لتطبيق الوظيفة.

// helpers in https://gist.github.com/tycho01/be27a32573339ead953a07010ed3b824, too many to include

// poor man's version, using a given return value rather than using `typeof` based on the given argument types:
function curry<Args extends any[], Ret>(fn: (...args: Args) => Ret): Curried<Args, Ret>;
type Curried<
  ArgsAsked,
  Ret,
  ArgsPrevious = [] // if we can't have empty tuple I guess any[] might also destructures to nothing; that might do.
> = <
  ArgsGiven extends any[] = ArgsGiven,
  ArgsAll extends [...ArgsPrevious, ...ArgsGiven]
      = [...ArgsPrevious, ...ArgsGiven]
  >(...args: ArgsGiven) =>
    If<
      TupleHasIndex<ArgsAll, TupleLastIndex<ArgsAsked>>,
      Ret,
      Curried<ArgsAsked, Ret, ArgsAll>
    >;

// robust alternative that takes into account return values dependent on input params, also needs #6606
function curry<F>(fn: F): Curried<F>;
type Curried<
  F extends (...args: ArgsAsked) => any,
  ArgsAsked extends any[] = ArgsAsked,
  ArgsPrevious = []
> = <
  ArgsGiven extends any[] = ArgsGiven,
  ArgsAll extends [...ArgsPrevious, ...ArgsGiven]
      = [...ArgsPrevious, ...ArgsGiven]
  >(...args: ArgsGiven) =>
    If<
      TupleHasIndex<ArgsAll, TupleLastIndex<ArgsAsked>>,
      F(...[...ArgsPrevious, ...ArgsGiven]), // #6606
      Curried<ArgsAsked, Ret, ArgsAll>
    >;

// bind:
interface Function {
    bind<
        F extends (this: T, ...args: ArgsAsked) => R,
        ArgsAsked extends any[],
        R extends any,
        T,
        Args extends any[], // tie to ArgsAsked
        Left extends any[] = DifferenceTuples<ArgsAsked, Args>,
        EnsureArgsMatchAsked extends 0 = ((v: Args) => 0)(TupleFrom<ArgsAsked, TupleLength<Args>>)
        // ^ workaround to ensure we can tie `Args` to both the actual input params as well as to the desired params. it'd throw if the condition is not met.
    >(
        this: F,
        thisObject: T,
        ...args: Args
    ): (this: any, ...rest: Left) => R;
    // ^ `R` alt. to calc return type based on input (needs #6606): `F(this: T, ...[...Args, ...Left])`
}

نعم ، لقد استخدمت مجموعة من أنواع المساعدين - فقط أحاول أن أفعل ما لدينا (+ تخيل ما يمكننا فعله بالقليل). أنا لست كثيرًا مقابل ...... أو ...*X أو [...T = ...A, ...B, ...C] أو [...PromiseLike<T>] أو <[...X, ...Y] = [...A]> أو <PromiseLike<T*>> . لكن IMO ، حتى ... يساعد في معالجة مشكلة حقيقية في الوقت الحالي ، وأود أن أرى معالجتها.

تحرير: لقد قمت بحل قيد الوسيطة لـ bind .

مجرد سؤال غبي على الأرجح. يبدو هذا واعدًا جدًا لتكون قادرًا على كتابة وظائف الكاري بشكل صحيح.
ولكن بالنسبة لبعض مشاريع العالم الحقيقي ، لا يمكن للمرء أن يرغب في قضاء الكثير من الوقت لكتابة جزء من التعليمات البرمجية الموجهة نحو البرمجة شديدة الوظائف.
لذا ، فإنني أتساءل ، نظرًا لأن --strict يتم تمكينه افتراضيًا في _tsconfig.json_ ، إذا كان هناك طريقة لتعطيل التحقق من النوع لجزء من الكود (بسبب الكسل أو ضيق الوقت).
لكن ، كما قلت ، ربما يكون سؤالًا غبيًا ... ^ _ ^

@ yahiko00 نوع من خارج الموضوع ، ولكن استخدم قسم exclude في tsconfig أو مختلف tsconfig s على مستويات مختلفة من المشروع.

أود أيضًا تقديم اقتراح آخر ، هل يمكننا الحصول عليه حتى يعمل كل من & و | مع وسيطة tuple واحدة باستخدام هذه الصيغة:

<...T>(...args:T): ...T&
// is the same as 
<t1, t2, t3>(...args:[t1, t2, t3]): t1 & t2 & t3;
// and
<....T>(...args:T): ...T|
// is the same as 
<t1, t2, t3>(...args:[t1, t2, t3]): t1 | t2 | t3;

سيكون اقتراح HyphnKnight أعلاه مفيدًا جدًا لشيء أقوم به أيضًا.

أريد أن أضيف إخلاء مسؤولية أن هذا الاقتراح لا يتم العمل عليه بنشاط. لكنني وجدت بالضبط نوع "الفن السابق" الذي كنت أرغب في قراءته عندما بدأت النظر في هذه المشكلة لأول مرة: http://www.ccs.neu.edu/racket/pubs/esop09-sthf.pdf

سأترك هذا هنا للرجوع إليه في المستقبل.

فتحت بعض العلاقات العامة التي جربت هذا:

  • [] # 17884 ينتشر في أنواع الصفوف (WIP)
  • [x] # 17898 استخراج معلمات الراحة (جاهزة)
  • [] # 18007 ينتشر في نوع الاتصال (WIP)
const c = 'a' + 'b';

هل يمكن حل المشكلة؟ نوع الاستنتاج من c هو 'ab' وليس string

سؤال متعلق بـ StackOverflow: معلمة الوظيفة الأخيرة الصريحة في TypeScript

sandersn ستغطي اقتراحك هذه الحالة ، بقدر ما أستطيع أن أرى ، هل هذا صحيح؟

بعد مرور أكثر من عامين على الاقتراح الأولي ، هل يجب أن نظل متفائلين؟

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

function* CombineEveryArgumentWithEveryArgument(...args: any[][]) {
    if (args.length < 1) {
        return [];
    }
    var haselements = false;
    for (var arg of args) {
        if (arg && arg.length > 0) {
            haselements;
        }
    }
    if (!haselements) {
        return [];
    }
    var indexes = [];
    for (var i = 0; i < args.length; i++) {
        indexes.push(0);
    }
    while (true) {
        var values = [];
        //One item from every argument.
        for (var i = 0; i < args.length; i++) {
            values.push(args[i][indexes[i]]);
        }
        if (indexes[0] + 1 < args[0].length) {
            yield values;
        }
        else {
            return values;
        }
        //Increment starting from the last, until we get to the first.
        for (var i = args.length; i > 0; --i) {
            if (indexes[i]++ >= args[i].length) {
                indexes[i] = 0;
            }
            else {
                break;
            }
        }
    }
}

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

for (let [target, child] of
    CombineEveryArgumentWithEveryArgument(targetsarray, childrenarray)) {

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

شيء من هذا القبيل سيكون جيدًا؟

function * generator<...T[]>(...args: T[]): [...T]

Griffork ، الممارسة الصحيحة ، حتى يتم تنفيذ هذا الاقتراح ، هي إنشاء العديد من الأحمال الزائدة للوظائف
على سبيل المثال ، انظر جميع أنواع الوعد
https://github.com/Microsoft/TypeScript/blob/master/lib/lib.es2015.promise.d.ts#L41 -L113

أجد أن بناء الجملة هذا محيرًا للغاية:

function apply<...T,U>(ap: (...args:...T) => U, args: ...T): U {

هذا يبدو طبيعيًا أكثر بالنسبة لي:

function apply<T, U>(ap: (...args: T) => U, args: T): U {

في وقت التشغيل ، تكون معلمة الراحة عبارة عن مصفوفة ، ويمكننا القيام بذلك حاليًا في TS:

function apply<T, U>(ap: (...args: T[]) => U, args: T[]): U {

لذلك يبدو من المنطقي إزالة القيود المفروضة على args كونها مصفوفة من T وبدلاً من ذلك تمكين مترجم TS لاستنتاج نوع tuple لـ T ، على سبيل المثال

function apply(ap: (...args: [number, number]) => number, args: [number, number]): number {

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

لا يزال من المنطقي بالنسبة لي ... لربط نوعين من أنواع tuple ، مثل [...T, ...U] .

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

function apply<...T,U>(ap: (...args:T) => U, ...args: T): U {

هل يعني أن T هو نوع tuple تم إنشاؤه ديناميكيًا ، لذلك إذا قمت بتمرير string و int في الوظيفة ، فإن T هو [string, int] .
هذا مثير للاهتمام بشكل خاص إذا كنت تريد التعبير ديناميكيًا عن نمط مثل هذا:

function PickArguments<T>(a: T[]): [T];
function PickArguments<T, U>(a: T[], b: U[]): [T, U];
function PickArguments<T, U, V>(a: T[], b: U[], c: V[]): [T, U, V];
//More overloads for increasing numbers of parameters.

//usage:
var [a, b, c] = PickArguments(["first", "second", "third"], [1, 2, 3], [new Date()]);
var d = b + 1; //b and d are numbers.
var e = c.toDateString(); //c is a date (autocompletes and everything), e is a string.

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

اقتراحك:

function apply<T, U>(ap: (...args: T) => U, args: T): U {

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

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

function apply<...T, U>(ap: (...args: ...T) => U, args: ...T): U {...}

أول ما يتبادر إلى الذهن عند قراءة apply<...T, هو أنه عامل انتشار ، لكنه في الواقع لا ينتشر على الإطلاق.

Griffork ، في سيظل T [string, int] .
هذا ما تعنيهfelixfbecker من خلال "تمكين مترجم TS بدلاً من ذلك من استنتاج نوع tuple لـ T" ، على الأقل بالطريقة التي أفهمها بها.

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

Griffork لا ، في رأيي ، يمكن أن يستنتج نوع tuple للمصفوفة args ، مع إعطاء كل معامل نوعه من خلال موقعه في المجموعة. ...args: T[] سيجبرهم جميعًا على أن يكونوا من نفس النوع T ، لكن ...args: T (والذي يعد حاليًا خطأ تجميعي) قد يستنتج نوع tuple لـ T .

أول ما يتبادر إلى الذهن عند قراءة تطبيق <... T ، هو أنه عامل انتشار ، لكنه في الواقع لا ينتشر على الإطلاق.

unional أتفق ، هذا هو بالضبط مصدر الالتباس.

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

function apply<T, U>(ap: (...args: T) => U, args: T): U {

أتوقع أن يكون T مصفوفة شيئًا ما (مثل string[] ).

وقراءة هذا:

function apply<T, U>(ap: (...args: T[]) => U, args: T[]): U {

أتوقع أن يتم T جميع الوسائط للنوع string ).

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

تضمين التغريدة
يحرر:
حسنا. ما زلت لا أعتقد أن هذا أمر بديهي.

أتوقع أن يكون T مصفوفة شيئًا ما (مثل سلسلة []).

الصفيف هو "مصفوفة شيء ما" ، إنه مجرد مصفوفة بطول ثابت وأنواع محددة لكل عنصر ، على سبيل المثال [string, number] (مقابل (string | number)[] ، وهو غير منضم ولا يعلن عن العنصر الذي يحتوي على العنصر نوع).

حسنًا ، ماذا تكتب إذا كنت تريد هذا السلوك بالفعل؟

لست متأكدًا من السلوك الذي تشير إليه بالضبط ، لكنني أفترض "فرض أن تكون جميع المعلمات من نفس النوع" ، والذي سيتم إنجازه بواسطة ...args: T[] .

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

لهذا السبب أعتقد أنه محير.
عندما تقوم بالانتشار ، تقوم بذلك فقط ، ولن تعلن عن شيء "قابل للنشر":

const a = { x: 1, y: 2 }
const b = { ...a }

// likewise
function appendString<T>(...args: T): [...T, string] {
  args.push('abc')
  return args
}

نعم. إذا كنت تريد التصريح بأن وسيطة النوع العام يجب أن تكون "قابلة للنشر" (والتي تعني وفقًا لمواصفات ES أنها يجب أن تكون قابلة للتكرار) ، فلدينا بالفعل طريقة للتعبير عن ذلك في TypeScript باستخدام extends :

function foo<T extends Iterable<any>>(spreadable: T): [...T, string] {
  return [...spreadable, 'abc']
}

const bar = foo([1, true])
// bar is [number, boolean, string]

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

وهو ما نقوله تم اقتراحه بالفعل: https://github.com/Microsoft/TypeScript/issues/5453#issuecomment -189703556

لكن الباقي طويل جدًا بحيث لا يمكن تناوله في مقعد واحد. 🌷

إذا كان التسلسل tuple هو الهبوط ، فيمكننا تطبيق أرقام الكنيسة! الصيحة!

type TupleSuc<T extends [...number]> = [...T, T['length']];
type TupleZero = [];  // as proposed, we need empty tuple
type TupleOne = TupleSuc<TupleZero>;
type Zero = TupleZero['length'];
type One = TupleOne['length'];

وإذا نجح تكرار النوع الشرطي ، فيمكننا إنشاء بنية tuple بالطول المطلوب:

type Tuple<N extends number, T = TupleZero> = T['length'] extends N ? T : Tuple<N, TupleSuc<T>>;
type TupleTen = Tuple<10>;
type Ten = TupleTen['length'];

لا أفترض أنني قرأت كل هذا الموضوع ، ولكن إذا كان وجود ...T في المعلمات العامة أمرًا محيرًا ،
لماذا لا تحاول عكس بنية التدمير على مستوى القيمة بشكل أكبر ، بحيث يكون استخدام [...T] في
يدمر موضع الوسيطة type-level النوع إذا كان نوعًا من نوع tuple؟ هذا سوف يتطلب أيضا
السماح بكتابة معلمات الراحة مع المجموعات ، مما يجعل المعامل التالي مكافئًا في موقع الاستدعاء
في TypeScript:

const first = (a: number, b: string) => …;
const second = (...ab: [number, string]) => …;

first(12, "hello"); // ok
second(12, "hello"); // also ok

INB4 "لكن هذا نوع موجه" - لا. هذا لا يغير الانبعاث على الإطلاق ، لا يزال first يحتوي على اثنين
المنبعثة من الوسائط المميزة ، سيظل لدى second وسيطة راحة واحدة. الشيء الوحيد الذي يتغير هو
أنه في موقع الاتصال ، سيتحقق TypeScript من أن معلمات second متطابقة ، بالترتيب ، tuple
[number, string] .

على أي حال ، بافتراض أننا نقبل بناء الجملة [...Type] ، فيمكننا كتابة apply النحو التالي:

function apply<
  [...ArgumentsT], // a type-level tuple of arguments
  ResultT
>(
  // the call site of `toApply` function will be used to infer values of `ArgumentsT`
  toApply:   (...arguments: ArgumentsT) => ResultT,
  arguments: ArgumentsT
) :
  ResultT
{
  // …
}

// NB: using my preferred formatting for complex type-level stuff; hope it's readable for you
// this is entirely equivalent to OP's notation version:
function apply<[...T], U>(ap: (...args: T) => U,  args: T): U {
  // …
}

// so at the call site of
const fn = (a: number, b: string, c: RegExp) => …;

// we have `ArgumentsT` equal to [number, string, RegExp]
apply(fn, [12, "hello" /s+/]); // ok, matches `ArgumentsT`
apply(fn, [12, /s+/]); // not ok, doesn't match `ArgumentsT`

سيتصرف بناء الجملة [...Type] كالتدمير على مستوى القيمة ، مما يسمح بالتقسيم
والانضمام إلى مجموعات على مستوى النوع ، حسب الحاجة:

type SomeType  = [string, number, "constant"];
type OtherType = ["another-constant", number];

type First<[First, ..._]> = FirstT;
type Rest<[_, ...RestT]> = RestT;
type Concat<[...LeftT], [...RightT]> = [...LeftT, ...RightT];
type FirstTwo<[FirstT, SecondT, ..._]> = [FirstT, SecondT];

// has type `string`
const aString: First<SomeType> =
  "strrriiing";
// has type `[number, "constant"]
const numberAndConstant: Rest<SomeType> =
  [42, "constant"];
// has type `[string, number, "constant", "another-constant", number]`
const everything: Concat<SomeType, OtherType> =
  ["herpderp", 42, "constant", "another-constant", 1337];
// has type `[string, number]`
const firstTwo: FirstTwo<SomeType> =
  ["striiiing", 42];

مثال على كيفية كتابة الدالة curry باستخدام هذا:

type Curried<
  [...ArgumentsT]
  ResultT,
  ArgumentT      = First<ArgumentsT>,
  RestArgumentsT = Rest<ArgumentsT>
> =
  // just ye olde recursione, to build nested functions until we run out of arguments
  RestArgumentsT extends []
    ? (argument: ArgumentT) => ResultT
    : (argument: ArgumentT) => Curried<RestArgumentsT, ResultT>;

// NB. with more complex generic types I usually use generic defaults as a sort-of
// of type-level variable assignment; not at all required for this, just nicer to read IMO

function curry<
  [...ArgumentsT],
  ResultT
>(
  function: (...arguments: ArgumentsT) => ResultT
) :
  Curried<ArgumentsT, ResultT>
{
  // do the magic curry thing here
}

// or in the short indecipherable variable name style

function curry<[...T], U>(fn: (...args: T) => U): Curried<T, U>
{
  // …
}

// this should let you do this (using `fn` from before)
const justAddRegex = curry(fn)(123, "hello");

justAddRegex(/s+/); // ok, matches the arguments of `fn`
justAddRegex(123); // not ok, doesn't match the arguments of `fn`

أفترض أنه سيكون من المفيد أيضًا أن تكون قادرًا على القول ، أن نوعًا ما من الوسيطات عبارة عن مجموعة على مستوى النوع
من نوع. ستكون المشكلة إذن كيف - مع الأخذ في الاعتبار أن 2.7 (أعتقد؟) تأخذ إمكانية التعيين
في الاعتبار طول المجموعة - للتعبير عن مفهوم أي بنية على مستوى النوع. ولكن ربما شيء من هذا القبيل
[...] يمكن أن يعمل؟ ليس لدي رأي قوي ، لكن سيكون من الجيد أن يكون المفهوم قابلاً للتسمية.

// bikeshed me
type OnlyTuplesWelcome<ArgumentT extends [...]> = ArgumentT;

في هذه الحالة ، يمكن أن تكون الصيغة أعلاه لـ [...ArgsT] اختصارًا لـ ArgsT extends [...] ،
إن استخدام التدمير على مستوى النوع يعني قيدًا على النوع ليكون مجموعة على مستوى النوع.

أفكار؟

jaen :

(...ab: [number, string]) => …

نعم ، هذا يشبه # 4130. لقد جربت شيئًا ما في # 18004 ، لكن أسلوبي كان صعبًا بعض الشيء (العقد الاصطناعية).

عند التعبير عن أي مجموعة ، رأيت شخصًا يستخدم any[] & { 0: any } ، والذي أعتقد أنه يعمل حتى نوع tuple فارغ fwiw. لم أزعج نفسي كثيرًا ، معظمها استقرت للتو مقابل any[] .

RxJS يحتاج هذا في كل مكان. الأكثر أهمية بالنسبة لـ Observable.prototype.pipe ، والذي لدينا حاليًا الكثير من الأحمال الزائدة ، ولكن يُطلب مني دائمًا إضافة "مستوى واحد آخر فقط".

للثاني إلى benlesh ، نستخدم RXJS على نطاق واسع ونحتاج إلى ذلك لوظائف الأنابيب.

أنا مؤلف كتاب PPipe ، والذي ، مثل وظائف الأنابيب في RXJS ، يحتاج إلى هذا. أعتقد أنني أرى نمطًا هنا ^ ^

أنا مؤلف كتاب Runtypes ، وهذه الميزة مطلوبة بشدة للتعبير عن النقابات والتقاطعات. الحل الوحيد (غير الكامل) هو التحميل الزائد الهائل:

https://github.com/pelotom/runtypes/blob/master/src/types/union.ts

🤢

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

هل تم حل مشكلة استنتاج وتطبيق حجج الباقي؟

function example(head: string, ...tail: number[]): number[] {
  return [Number(head), ...tail]
}

function apply<T, U>(fn: (...args: T) => U, args: T): U {
  return fn.apply(null, args)
}

إذا تم الاستدلال على نوع T في apply(example, ['0', 1, 2, 3]) على أنه [string, number[]] ، فإن استدعاء التقديم سيثير خطأ.

هذا يعني أن نوع T هو حقًا

type T = [string, ...number[]]

أو

type T =
  {0: string} &
  {[key: Exclude<number, 0>]: number} &
  Methods

وحش غريب حقًا ، لكن بالنظر إلى كيفية ({0: string} & Array<number>)[0]
يتحول حاليًا إلى string [1] ويبدو أنه من الممكن التشفير دون تغيير كبير
إلى نظام الكتابة.

[1] هل هذا خطأ ، هل يجب أن يكون string | number ؟

آسف لإزعاج 36 مشاركًا في هذه المشكلة (نصيحة: استخدم هذا ) ، ولكن كيف يمكننا مراقبة ما إذا كان هذا لا يزال قيد الدراسة ، وما إذا كان هذا على خارطة الطريق ، وما إلى ذلك؟

من المحزن أنه لم يتم تعيين أي شخص لها بعد بعد عامين ونصف ، يبدو أنها ميزة مهمة جدًا :(

ملاحظة: لقد قرأت بضع عشرات من التعليقات ، وحاولت Cmd + F وما إلى ذلك ، ولم أجد هذه المعلومات.

brunolemos هناك إشارة إلى الأنواع المتنوعة في اجتماع التصميم الأخير
https://github.com/Microsoft/TypeScript/issues/23045
من أجل جعل هذه الميزة ، يحتاجون أولاً إلى إنشاء مفاهيم أكثر بدائية وتكرارًا ، وعندما يكون هناك أسس كافية ، فأنا متأكد من أنهم سيضيفونها إلى أي معلم

لا استطيع فعل هذا

type Last<T extends any[]> =
    T extends [infer P] ? P :
    ((...x: T) => any) extends ((x: any, ...xs: infer XS) => any) ? Last<XS> :

إنها مسألة # 14174 ولكن كعلاقة

kgtkr للرجوع اليها راجع خدعة fightingcat لجعل المترجم يتجاهل العودية.

شكرا

type Last<T extends any[]> = {
    0: never,
    1: Head<T>,
    2: Last<Tail<T>>,
}[T extends [] ? 0 : T extends [any] ? 1 : 2];

حسنًا ، لدي سؤال. لدي رمز مثل هذا للتعامل مع mixins:

export const Mixed = <

    OP = {}, OS = {}, // base props and state
    AP = {}, AS = {}, // mixin A props and state
    BP = {}, BS = {}, // mixin B props and state
    // ...and other autogenerated stuff
>(

    // TODO: Find a way to write that as ...args with generics:
    a?: ComponentClass<AP, AS>,
    b?: ComponentClass<BP, BS>,
    // ...and other autogenerated stuff

) => {

    type P = OP & AP & BP;
    type S = OS & AS & BS;
    const mixins = [a, b];

    return class extends Component<P, S> {
        constructor(props: P) {
            super(props);
            mixins.map(mix => {
                if (mix) {
                    mix.prototype.constructor.call(this);
                    // some state magic...
                }
            });
        }
    };
};

أستخدمه كما يلي:

class SomeComponent extends Mixed(MixinRedux, MixinRouter, MixinForm) {
     // do some stuff with mixed state
}

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

مع الإصدار 3.0 ، أصبح من الممكن الآن التصريح عن بقية الأرجل على أنها tupple

declare function foo(...args: [number, string, boolean]): void;

ولكن هل من الممكن الحصول على نوع من الحجج من نوع tuple للدالة المعينة بشكل عكسي؟

شيء مثل Arguments<foo> سيكون لطيفًا.

whitecolor ماذا عن هذا؟

type Arguments<F extends (...x: any[]) => any> =
  F extends (...x: infer A) => any ? A : never;

مع TS 3.0 يمكننا القيام بذلك الآن

function compose<X extends any[], Y extends any[], Z extends any[]>(
  f: (...args: X) => Y,
  g: (...args: Y) => Z
): (...args: X) => Z {
  return function (...args) {
    const y = f(...args);
    return g(...y);
  };
}

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

https://www.typescriptlang.org/play/index.html#src =٪ 0D٪ 0A٪ 0D٪ 0A٪ 0D٪ 0A٪ 0D٪ 0A٪ 0D٪ 0A٪ 0D٪ 0A٪ 0D٪ 0Afunction٪ 20foo0 () ٪ 3A٪ 20void٪ 20٪ 7B٪ 0D٪ 0A٪ 20٪ 0D٪ 0A٪ 7D٪ 0D٪ 0A٪ 0D٪ 0 وظيفة٪ 20bar0 ()٪ 3A٪ 20void٪ 20٪ 7B٪ 0D٪ 0A٪ 0D٪ 0A٪ 7D ٪ 0D٪ 0A٪ 0D٪ 0 وظيفة٪ 20foo1 (a٪ 3A٪ 20string)٪ 3A٪ 20٪ 5Bstring٪ 5D٪ 20٪ 7B٪ 0D٪ 0A٪ 20٪ 20return٪ 20٪ 5Ba٪ 5D٪ 3B٪ 0D٪ 0A٪ 7D٪ 0D٪ 0A٪ 0D٪ 0 وظيفة٪ 20bar1 (a٪ 3A٪ 20string)٪ 3A٪ 20٪ 5Bstring٪ 5D٪ 20٪ 7B٪ 0D٪ 0A٪ 20٪ 20return٪ 20٪ 5Ba٪ 5D٪ 3B٪ 0D٪ 0A ٪ 7D٪ 0D٪ 0A٪ 0D٪ 0 وظيفة٪ 20foo2 (a1٪ 3A٪ 20string٪ 2C٪ 20a2٪ 3A٪ 20boolean)٪ 3A٪ 20٪ 5Bstring٪ 2C٪ 20boolean٪ 5D٪ 20٪ 7B٪ 0D٪ 0A٪ 20٪ 20return٪ 20٪ 5Ba1٪ 2C٪ 20a2٪ 5D٪ 3B٪ 0D٪ 0A٪ 7D٪ 0D٪ 0A٪ 0D٪ 0Afunction٪ 20foo21 (a1٪ 3A٪ 20string٪ 2C٪ 20a2٪ 3A٪ 20boolean)٪ 3A٪ 20٪ 5Bstring ٪ 5D٪ 20٪ 7B٪ 0D٪ 0A٪ 20٪ 20return٪ 20٪ 5Ba1٪ 5D٪ 3B٪ 0D٪ 0A٪ 7D٪ 0D٪ 0A٪ 0D٪ 0A٪ 0D٪ 0Afunction٪ 20bar2 (a1٪ 3A٪ 20string٪ 2C ٪ 20a2٪ 3A٪ 20 منطقي)٪ 3A٪ 20٪ 5Bstring٪ 2C٪ 20 منطقي٪ 5D٪ 20٪ 7B٪ 0D٪ 0A٪ 20٪ 20 رجوع٪ 20٪ 5Ba1٪ 2C٪ 20a2٪ 5D٪ 3B٪ 0D٪ 0A٪ 7D٪ 0D٪ 0A٪ 0D٪ 0A٪ 0D٪ 0 وظيفة٪ 20 الغرض٪ 3CX٪ 20 نهاية٪ 20any٪ 5B٪ 5D٪ 2C٪ 20Y٪ 20 نهاية٪ 20any٪ 5B٪ 5D٪ 2C٪ 20Z٪ 20 نهاية٪ 20any٪ 5B٪ 5D٪ 3E ( ٪ 0D٪ 0A٪ 20٪ 20f٪ 3A٪ 20 (... args٪ 3A٪ 20X)٪ 20٪ 3D٪ 3E٪ 20Y٪ 2C٪ 0D٪ 0A٪ 20٪ 20g٪ 3A٪ 20 (... args٪ 3A٪ 20Y)٪ 20٪ 3D٪ 3E٪ 20Z٪ 0D٪ 0A )٪ 3A٪ 20 (... args٪ 3A٪ 20X)٪ 20٪ 3D٪ 3E٪ 20Z٪ 20٪ 7B٪ 0D٪ 0A٪ 20٪ 20return٪ 20function٪ 20 (... args)٪ 20٪ 7B٪ 0D٪ 0A٪ 20٪ 20٪ 20٪ 20const٪ 20y٪ 20٪ 3D٪ 20f (... args)٪ 3B٪ 0D٪ 0A٪ 20٪ 20٪ 20٪ 20 العودة٪ 20g (... y)٪ 3B٪ 0D٪ 0A٪ 20٪ 20٪ 7D٪ 3B٪ 0D٪ 0A٪ 7D٪ 0D٪ 0A٪ 0D٪ 0A٪ 0D٪ 0A٪ 0D٪ 0A٪ 0D٪ 0Aconst٪ 20baz0٪ 20٪ 3D٪ 20composition (٪ 0D٪ 0A ٪ 20٪ 20foo0٪ 2C٪ 0D٪ 0A٪ 20٪ 20bar0٪ 0D٪ 0A)٪ 3B٪ 0D٪ 0A٪ 0D٪ 0Aconst٪ 20baz21٪ 20٪ 3D٪ 20 تكوين (٪ 0D٪ 0A٪ 20٪ 20foo21٪ 2C٪ 0D ٪ 0A٪ 20٪ 20bar1٪ 0D٪ 0A)٪ 3B٪ 0D٪ 0Aconst٪ 20baz2٪ 20٪ 3D٪ 20 تشكيلة (٪ 0D٪ 0A٪ 20٪ 20foo2٪ 2C٪ 0D٪ 0A٪ 20٪ 20bar2٪ 0D٪ 0A)٪ 3B٪ 0D٪ 0A٪ 0D٪ 0A٪ 0D٪ 0Aalert (baz2 ('a'٪ 2C٪ 20false))٪ 0D٪ 0Aalert (baz21 ('a'٪ 2C٪ 20true))٪ 0D٪ 0Aalert (baz0 ())

تضمين التغريدة
استخدم الأنواع الشرطية:

function compose<X extends any[], Y extends any, Z extends any>(
  f: (...args: X) => Y,
  g: Y extends any[] ? (...args: Y) => Z : () => Z
): (...args: X) => Z {
    return function (...args) {
        const y = (f as any)(...args);
        return (g as any)(...y);
    } as any;
}

بالتأكيد ، ولكن هذا نوع من الاختراق مع هؤلاء as any :) أعتقد أنه سيكون رائعًا إذا كان نظام الكتابة يدعم هذا بدون اختراق

حسنًا ، يمكن أن تكون الأنواع في جسم الوظيفة أشياء متعددة ، ولا يوجد الكثير مما يمكن فعله حيال ذلك - يمكنك فعل شيء مثل (f as (...args: any[]) => Y) بدلاً من ذلك ، لكن أعتقد أن هذا يقلل من الوضوح بدون سبب حقيقي

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

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

لقد نجحت في كتابة الأنابيب
https://github.com/kgtkr/typepark/blob/master/src/pipe.ts

kgtkr : هذا يبدو رائعا! :)

نوع الأنابيب يعطل ملعب TS بالنسبة لي (على الرغم من أن الآخرين يعملون بشكل جيد) ، أعتقد أنه يحتاج إلى أحدث TS؟

يعرض TS أيضًا بعض أخطاء عمق العودية - يبدو أن isiahmeadows فتح # 26980 لذلك.

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

نوع الأنابيب يعطل ملعب TS بالنسبة لي (على الرغم من أن الآخرين يعملون بشكل جيد) ، أعتقد أنه يحتاج إلى أحدث TS؟

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

يعرض TS أيضًا بعض أخطاء عمق العودية - يبدو أن isiahmeadows فتح # 26980 لذلك.

هذا لشيء مرتبط ولكنه مختلف: رفع قيد بأنواع شرطية لسببين:

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

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

عند التفكير الثاني ، يبدو هذا النوع من الأنابيب وكأنه طريقة معقدة حقًا للقيام بـ (vs...: Params<T[0]>) => ReturnType<Last<T>> . من المحتمل أن يصبح أي تكرار يتجاوز ذلك (بصرف النظر عن فحوصات البارامتر الوسيطة) أكثر فائدة مع أنواع الإرجاع المعتمدة على المدخلات.

@ tycho01 إنها تحاول كتابة أشياء مثل هذه ، حيث يكون النوع في الأساس هذا:

   f1,     f2,   ...,   fm,     fn    -> composed
(a -> b, b -> c, ..., x -> y, y -> z) -> (a -> z)

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


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

@ tycho01kgtkr راجع للشغل، وأنا محدث هذا الخطأ مع تصحيح PipeFunc المتكررة ، نسخ هنا للراحة:

type Last<L extends any[], D = never> = {
    0: D,
    1: L extends [infer H] ? H : never,
    2: ((...l: L) => any) extends ((h: any, ...t: infer T) => any) ? Last<T> : D,
}[L extends [] ? 0 : L extends [any] ? 1 : 2];

type Append<T extends any[], H> =
    ((h: H, ...t: T) => any) extends ((...l: infer L) => any) ? L : never;

type Reverse<L extends any[], R extends any[] = []> = {
    0: R,
    1: ((...l: L) => any) extends ((h: infer H, ...t: infer T) => any) ?
        Reverse<T, Append<R, H>> :
        never,
}[L extends [any, ...any[]] ? 1 : 0];

type Compose<L extends any[], V, R extends any[] = []> = {
    0: R,
    1: ((...l: L) => any) extends ((a: infer H, ...t: infer T) => any) ?
        Compose<T, H, Append<R, (x: V) => H>>
        : never,
}[L extends [any, ...any[]] ? 1 : 0];

export type PipeFunc<T extends any[], V> =
    (...f: Reverse<Compose<T, V>>) => ((x: V) => Last<T, V>);

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

لم أختبرها على نوع _.flow أو _.flowRight محتمل حتى الآن ، ولكن يجب أن يعمل ذلك كنقطة بداية.

@ tycho01
مطلوب
مطبوع @ التالي
3.0.1 / 3.0.2 لا يعمل

بفضل هذا الموضوع ، صنعت هذا

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

Yuudaari سوف أشير إلى أن كتابة لوداش _.flow ، Ramda _.compose ، وما إلى ذلك هي أحد الأشياء الدافعة لهذا الخطأ ، والكتابة الناجحة هي جزء من حل هذه المشكلة. في الواقع ، هذا أحد الأسباب المذكورة في وصف المشكلة الأصلي.

حقًا ، في هذه المرحلة ، أعتقد أن 99٪ من المشكلات الموجودة اليوم مع المتغيرات تتعلق ببيئة العمل ، وليس الوظائف. يمكننا أن اكتب Function.prototype.bind و Promise.all تماما مع خليط من أنواع المفهرسة، وأنواع المشروطة، والعودية (يمكنك القيام المتكررة Append بالتكرار القائمة ل Function.prototype.bind ، و Promise.all سيكون تكرارًا بسيطًا + Append ) ، فقط من الصعب جدًا القيام بذلك.

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

أعتقد أن الأشخاص الذين ينتظرون الإعلانات هنا قد فاتتهم الأخبار الكبيرة - فقد تبين أن الوظائف الممكنة الآن Concat<T, U> هي بالضبط [...T, ...U] .

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

أعتقد أن هذا يعني أننا لن نكون أسوأ من إغلاق هذا الخيط ، لذلك ربما تكون هذه لحظة جيدة للتساؤل - ما الذي لا يزال الناس يريدون من هذا الاقتراح؟

[إنه] مجرد أمر محرج للغاية ومعياري

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

في هذه المرحلة ، ربما قد يؤدي هذا الاقتراح إلى تحسين الأداء في الغالب؟

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

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

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

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

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

تحرير: إضافة ارتباط للسياق.

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

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

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

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

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

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

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

أنا شخصياً أود السكر النحوي لهذه:

  1. تسلسل قائمتين عبر [...First, ...Second] .
  2. إلحاق القيم عبر [...Values, Item] .
  3. استخراج العنصر الأخير عن طريق T extends [...any[], infer Last] .
  4. استخراج الذيل عن طريق T extends [A, B, ...infer Tail] .

ادمج ذلك مع # 26980 ، ويمكنني تحويل الأنواع المذكورة أعلاه إلى هذا:

type Compose<L extends any[], V, R extends any[] = []> =
    L extends [infer H, ...infer T] ?
        Compose<T, H, [...R, (x: V) => H]> :
        R;

export type PipeFunc<T extends any[], V> =
    T extends [...any[], infer R] ?
        (...f: Compose<T, V>) => ((x: V) => R) :
        () => (x: V) => V;

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

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

أعتقد أن الكلمة الرسمية هي شيء مثل "لا تفعل ذلك". ahejlsberg قال :

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

لا تفعل ذلك!

☹️

jcalz إذن هل كل سبب لوجود # 26980؟

عندما بدأت باستخدام TS في نهاية هذا العام ، كنت أميل إلى كتابة __________________ هذا_ فقط! ( ...T ) على أمل أن يكون هذا هو بناء الجملة المتغير من نوع tuples. حسنًا ، أتمنى أن يدخل هذا :)

اكتشفت للتو استخدامًا جديدًا لـ [...T, ...U] : كتابة أدوات إنشاء HTML بشكل صحيح. للحصول على مثال ملموس ، يجب أن يكون أطفال <video> على النحو التالي:

  • إذا كان العنصر يحتوي على سمة src :

    • صفر أو أكثر من عناصر <track>

  • إذا كان العنصر لا يحتوي على سمة src :

    • صفر أو أكثر من عناصر <source>

  • لا توجد عناصر أو أكثر وفقًا لنموذج محتوى الأصل ، باستثناء عدم السماح باستخدام عنصر تابع لـ audio أو video .

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

type VideoChildren<ParentModel extends string[]> = [
    ...Array<"track">, // Not possible
    ...{[I in keyof ParentModel]: P[I] extends "audio" | "video" ? never : P[I]},
]

3.5 سنوات: /

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

type DrawOp<...T> = (G: CanvasRenderingContext2D, frame: Bounds, ...args: any[]) => void;
const drawOps: DrawOp<...any>[] = [];

function addDrawOp<...T>(fn: DrawOp<...T>, ...args: T) {
    drawOps.push(fn);
}

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

  function $findOne(
    ctx: ICtx,
    filter: FilterQuery<TSchema>,
    cb: Cb<TSchema>,
  ): void;
  function $findOne<T extends keyof TSchema>(
    ctx: ICtx,
    filter: FilterQuery<TSchema>,
    projection: Projection<T>,
    cb: Cb<Pick<TSchema, T>>,
  ): void;
  function $findOne(
    ctx: ICtx,
    filter: FilterQuery<TSchema>,
    projection: undefined,
    cb: Cb<TSchema>,
  ): void;
  function $findOne<T extends keyof TSchema>(
    ctx: ICtx,
    filter: mongodb.FilterQuery<TSchema>,
    projection: Projection<T> | Cb<TSchema> | undefined,
    cb?: Cb<Pick<TSchema, T>>,
  ): void {

  promisify($findOne) // this can't infer types correctly

هذا لا يعمل حاليًا على الإطلاق ويكتب فقط promisify كـ (ctx: ICtx, filter: FilterQuery<TSchema>) => Promise<TSchema[]> الذي يفقد المعلومات من هذه التوقيعات.

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

يتفاقم هذا بسبب حقيقة أن معلمات الباقي لا يمكن أن تكون سوى المعلمة الأخيرة (على سبيل المثال ، (cb, ...args) صالح ولكن ليس (...args, cb) ، لذلك حتى إذا كان التوقيع داخليًا من نوع الاتحاد ، فلا يمكنك فعلاً انشر الأشياء بشكل صحيح - على سبيل المثال ، سيكون الأمر واضحًا إلى حد ما إذا كان cb دائمًا الوسيطة الأولى لكتابة التعهد كـ function promisify<T, V extends any[]>(fn: (cb: (err: Error | null, res?: T) => void, ...args: V)): (...args: V) => T ويمكنك على الأقل الحصول على أنواع الاتحاد للتوقيعات مع نفس استجابة الإرجاع ، ولكن لأن إنها المعلمة النهائية بعد عدم وجود الكثير مما يمكن القيام به هنا

@ Qix- تم تفعيل السيناريو الخاص بك بواسطة # 24897. تم تنفيذه في TS 3.0.

تضمين التغريدة رائع شكرا لك ♥ ️

لقد مضى وقت طويل في الانتظار ... لكن من الممكن كتابة أنواع مختلفة اليوم. TS ناضجة بما يكفي لكتابة أنواع معقدة تعمل. لذلك أخذت الوقت الكافي لكتابة أنواع الكاري ، والتسلسل ، والتأليف ، والأنابيب لرامدا.

ويتم شحنها الآن مع حزام الأدوات ts .

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

هل لديك على medium.com بالفعل؟ URL؟

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

@ pirix-gh لكن هذا ليس نوعًا مختلفًا من الأدوية كما في هذا الاقتراح

declare function m<...T>(): T

m<number, string>() // [number, string]

goodmind نعم ، إنها ليست كذلك ، يتم ... مثل هذا:

declare function m<T extends any[], U extends any[]>(): Concat<T, U>

m<[number, string], [object, any]>() // [number, string, object, any]

بالضبط مثل:

declare function m<...T, ...U>(): [...T, ...U]

m<number, string, object, any>() // [number, string, object, any]

في غضون ذلك ، أثناء انتظار هذا العرض: hourglass_flowing_sand:

@ pirix-gh هل يمكنك المساعدة في وظيفة التغليف مثل

type fn = <T>(arg: () => T) => T
let test1: fn
let res1 = test1(() => true) // boolean

type fnWrap = (...arg: Parameters<fn>) => ReturnType<fn>
let test2: fnWrap
let res2 = test2(() => true) // {}

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

يحدث هذا لأنه عند استخراج المعلمات / العائد fn التي تعتمد على الأدوية ، فإن TS يستنتجها إلى أقرب أنواعها (في هذه الحالة T سيكون any ). لذا لا توجد طريقة لذلك في الوقت الحالي. أفضل أمل لنا هو انتظار دمج هذا الاقتراح مع https://github.com/Microsoft/TypeScript/pull/30215. لذا ، سيتعين عليك كتابة نوع التحميل الزائد.

أو ربما يمكننا إيجاد طريقة للحفاظ على / نقل الأدوية الجنيسة بطريقة يمكننا القيام بها:

declare function ideal<...T>(a: T[0], b: T[1], c: T[2]): T

ideal('a', 1, {}) // T = ['a', 1, {}]

بهذه الطريقة ، نعيد بناء fn من قطع منه. الجزء المفقود اليوم هو الجزء العام مثل @ goodmind المشار إليه.

@ pirix-gh إذا لم أكن مخطئًا ، يمكنك فقط القيام بذلك لتحقيق ما لديك هناك:

declare function MyFunction<A, B, C, Args extends [A, B, C]>(...[a, b, c]: Args): Args

const a = MyFunction(1, 'hello', true);
// typeof a = [number, string, boolean]

ClickerMonkey ليس بالضبط ، لأن ما اقترحته يصلح لعدد غير محدود من الحجج. لكن ربما يمكننا أيضًا القيام بذلك ، بما اقترحته (لم أره في الاقتراح):

declare function MyFunction<A, B, C, ...Args>(...[a, b, c]: Args): Args

const a = MyFunction(1, 'hello', true);
// typeof a = [number, string, boolean]

@ pirix-gh الوسيطات من النوع A و B و C في المثال الخاص بك غير مستخدمة.

-declare function MyFunction<A, B, C, ...Args>(...[a, b, c]: Args): Args
+declare function MyFunction<...Args>(...[a, b, c]: Args): Args

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

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

goodmind نعم ، إنها ليست كذلك ، يتم ... مثل هذا:

declare function m<T extends any[], U extends any[]>(): Concat<T, U>

m<[number, string], [object, any]>() // [number, string, object, any]

بالضبط مثل:

declare function m<...T, ...U>(): [...T, ...U]

m<number, string, object, any>() // [number, string, object, any]

في هذه الأثناء أثناء انتظار هذا الاقتراح ⏳

من أين حصلت على Concat<> ؟

تحرير: لا تهتم بالعثور على الكود المصدري.

@ pirix-gh لذا حاولت أن أفعل ذلك باقتراحاتك لكني لم أستطع حلها.

~ المشكلة هي أنني أحاول توسيع معلمات ctor لفئة ما وهي تعمل لدرجة أن لدي مصفوفة من الأنواع ولكن لا يمكنني نشرها لمعلمات ctor.

Class Test {
  constructor(x: number, y: string) {}
}
let ExtendedClass = extendCtor<[number, string], [number]>(Test);

let instance = new ExtendedClass(1, '22', 2);

تحديث: لا تهتم بهذا أيضًا باستخدام السبريد في وظيفة ctor.

هنا رابط الحل

المشكلة الوحيدة هي أعطال TS في كل مرة تقريبًا: |
وهذا ما تقوله TypeScript Type instantiation is excessively deep and possibly infinite.ts(2589)

التحديث 2:
لقد حققت ذلك من خلال وضع النوع الجديد في البداية ، ولا يزال من الجيد أن أتمكن من دمج هذه الأنواع.

// ...
type CtorArgs<T, X> = T extends (new (...args: infer U) => any) ? [...U, X] : never;
// To be used as CtorArgs<typeof Test, string>
// ...
let instance = new MyClass1('22', 2, 'check');

في مقابل:

let MyClass1 = extendClass<typeof Test, string>(Test);

let instance = new MyClass1('check', '22', 2);

رابط إلى الحل النهائي.

إذا فهمت بشكل صحيح ، يمكن التصريح عن Object.assign بما يشبه ما يلي لدعم المتغيرات بشكل كامل.

type Assign<T, U extends any[]> = {
  0: T;
  1: ((...t: U) => any) extends ((head: infer Head, ...tail: infer Tail) => any)
    ? Assign<Omit<T, keyof Head> & Head, Tail>
    : never;
}[U['length'] extends 0 ? 0 : 1]

interface ObjectConstructor {
  assign<T, U extends any[]>(target: T, ...source: U): Assign<T, U>
}

هل هناك أي سبب للإعلان عن ذلك بطريقة مختلفة في lib.d.ts في TypeScript؟

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

jcalz لقد أنشأت اختبارًا مكثفًا يُظهر النوع Minus قيد التنفيذ. يؤدي Minus 216000 مرة في أقل من 4 ثوان. يوضح هذا أن TS يمكنه التعامل مع الأنواع العودية جيدًا. لكن هذا حديث جدا.

لماذا ا؟ هذا بفضل Anders: tada: (https://github.com/microsoft/TypeScript/pull/30769). سمح لي بالتبديل من الأنواع الشرطية إلى الشروط المفهرسة (مثل التبديل). وفي واقع الأمر ، فقد قام بتحسين الأداء بمقدار x6 لحزام الأدوات ts. شكرا جزيلا له.

لذلك من الناحية الفنية ، يمكننا إعادة كتابة نوع kimamula بأمان باستخدام ts-toolbelt. التعقيد يتبع O (n):

import {O, I, T} from 'ts-toolbelt'

// It works with the same principles `Minus` uses
type Assign<O extends object, Os extends object[], I extends I.Iteration = I.IterationOf<'0'>> = {
    0: Assign<O.Merge<Os[I.Pos<I>], O>, Os, I.Next<I>>
    1: O
}[
    I.Pos<I> extends T.Length<Os>  
    ? 1
    : 0
]

type test0 = Assign<{i: number}, [
    {a: '1', b: '0'},
    {a: '2'},
    {a: '3', c: '4'},
]>

يجعل lib أيضًا العودية آمنة باستخدام Iteration الذي سيمنع أي تجاوز من TypeScript. بمعنى آخر ، إذا تجاوز I أكثر من 40 فإنه يفيض و Pos<I> يساوي number . وبالتالي وقف العودية بأمان.

نوع متكرر مماثل كتبته ( Curry ) يتم شحنه مع Ramda ، ويبدو أنه يعمل بشكل جيد.

بالمناسبة ، شكرت لكم (jcalz) على صفحة المشروع على كل مشورتكم الحسنة.

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

شيء ما تم تضمينه في اختبارات خط الأساس الخاصة بهم بحيث إذا تعطل فسيتم إصلاحه.

أعتقد أن لدينا شيئًا قريبًا جدًا يستخدم داخليًا

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

type Foo<T> = { a: Foo<Bar<T>>, b: Baz }[Qux<T> extends Quux ? "a" : "b" ]

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

أوه ، عادل - الأمر مختلف في هذا الصدد ، نعم. أنا أقول فقط نمط "الكائن المفهرس على الفور لتحديد الأنواع" وأدركت أن لدينا _ هذا_.

عندي سؤال.

كم من الأشياء المقترحة هنا لا تزال ذات صلة؟ تم فتح هذا الإصدار منذ 4 سنوات وأشعر أن مجموعة من الأشياء قد تغيرت منذ ذلك الحين.

من تعليقي هنا ،
https://github.com/microsoft/TypeScript/issues/33778#issuecomment -537877613

انا قلت،

TL ؛ DR ، وأنواع tuple ، و rest args ، وأنواع المصفوفات المعينة ، واستدلال tuple لـ non-rest arg ، والأسماء المستعارة من النوع العودي = لا حاجة حقيقية لدعم وسيطة من النوع المتغير

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

حتى نحصل على نسخة مباركة رسميًا من Concat<T extends any[], U extends any[]> فلا يزال هذا مناسبًا. لا أعتقد أن ميزة مرجع النوع التكراري القادمة تمنحنا هذا ، ولكن يسعدني أن يتم إخبارنا (بشكل موثوق) بخلاف ذلك.

أليس لدينا تطبيقات Concat<> بالفعل؟

أم أن العبارة الرئيسية هنا "مباركة رسميًا"؟

لأن تأكيدي هو أنه يمكنك في الأساس فعل كل شيء (أو كل شيء تقريبًا؟) قد ترغب في الوقت الحالي ، حتى لو لم يكن "مباركًا رسميًا" تمامًا.

لكن أعتقد أنه يجب دائمًا تفضيل "المباركة رسميًا" ... نقطة جيدة. أنا معتاد جدًا على (ab) استخدام تلك الأسماء المستعارة من النوع العودي

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

كبير 👍 على هذا!

هذه الميزة مهمة جدا.

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

لأن تأكيدي هو أنه يمكنك بشكل أساسي القيام بكل شيء (أو كل شيء تقريبًا؟) الذي قد ترغب فيه في الوقت الحالي

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

@ ماثيو دين ليس صحيحا تماما. إليك مثال قد تحققه إلى حد ما.

كما أفهم ، تسعى TS إلى كتابة أكبر قدر ممكن من برامج Vanilla JS. هنا لغز:

const f = <T extends any[]>(...args: T): T => args;
const g = <T extends any[]>(...a: T): WhatExactly<T> => {
    return f(3, ...a, 4, ...a, 5);
}
g(1, 2);

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

@ polkovnikov-ph يُستدل على النوع حاليًا على النحو التالي: [number, ...any[]] ، وهو أمر غير مفيد.

أود أيضًا أن ألاحظ أنه لا يتعين علينا احترام قاعدة جرينسبون العاشرة لمدة 15 عامًا كما فعلت C ++ ، لأن C ++ مرت بالفعل بكل Head<> s و Cons<> بالنسبة لنا ، و ابتكر بعض بناء جملة قوالب متغيرة سهلة الاستخدام ونظيفة. يمكننا توفير (مئات السنين) من وقت المطور ونأخذ أفضل الأجزاء من هناك.

على سبيل المثال ، الأنواع المتغيرة لها نوع مختلف في C ++ ، لذلك لا يمكنك استخدام متغيرات النوع المتغير حيث يكون النوع متوقعًا ، على عكس النوع الذي extends any[] في TS. يسمح هذا لـ C ++ بالتخطيط / الضغط على المجموعات من خلال ذكر متغير النوع المتغير داخل بعض التعبيرات المضمنة في عامل القطع. هذا إلى حد كبير بديل tuple لأنواع الكائنات المعينة.

type Somethify<...T> = [...Smth<T>]
type Test1 = Somethify<[1, 2]> // [Smth<1>, Smth<2>]

type Zip<...T, ...U> = [...[T, U]]
type Test2 = Zip<[1, 2], [3, 4]> // [[1, 3], [2, 4]]

type Flatten<...T extends any[]> = [......T]
type Test3 = Flatten<[[1, 2], [3, 4]]> // [1, 2, 3, 4]

يرجى ذكر أن صيغة القطع المقترحة بدلاً من extends any[] مستخدمة في المثال ليس فقط لأسباب جمالية ، ولكن بسبب

type A<T> = any[]
type B<T extends any[]> = [...A<T>]
type C = B<[1, 2]>

هو بالفعل برنامج TS صالح. ينتهي الأمر بكون C any[] بدلاً من [any[], any[]] الذي سينشئه النوع المتغير المعين.

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

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

type SharedValues = [S1, S2, S3];
type TupleOfSpecificKind = [V1, ...SharedValues, V2];

sethfowler إذا كان لديك بعض الأمثلة لما تريد التعبير عنه ، فهذا مفيد دائمًا لنا. وإلا فقد تكون مهتمًا بـ https://github.com/microsoft/TypeScript/issues/26113

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

type OpLineSegment = [
  StrokeColor,
  FillColor,
  number,  // thickness
  number, number, number,  // X0, Y0, Z0
  number, number, number  // X1, Y1, Z1
];
type OpCircle = [
  StrokeColor,
  FillColor,
  number, number, number,  // X, Y, Z of center
  number // radius
];
type OpPolygon = (StrokeColor | FillColor | number)[];  // [StrokeColor, FillColor, repeated X, Y, Z]]
type OpFan = (StrokeColor | FillColor | number)[];  // StrokeColor, FillColor, repeated X, Y, Z up to 10x

نود أن نكون قادرين على التعبير عن هذه الأنواع أكثر مثل هذا:

type Colors = [StrokeColor, FillColor];
type Vertex3D = [number, number, number];

type OpLineSegment = [...Colors, number /* thickness */, ...Vertex3D, ...Vertex3D];
type OpCircle = [...Colors, ...Vertex3D, number /* radius */];
type OpPolygon = [...Colors, ...Repeated<...Vertex3D>];
type OpFan = [...Colors, ...RepeatedUpToTimes<10, ...Vertex3D>];

لدينا عدد كبير من هذه الأوامر ، لذا فإن مجرد انتشار على مستوى النوع سيؤدي إلى كود أكثر قابلية للصيانة بشكل كبير. إن وجود أنواع متباينة حتى نتمكن من كتابة وظائف على مستوى النوع مثل Repeated<> و RepeatedUpToTimes<> (والتي من شأنها تقييم الاتحادات المعرفة بشكل متكرر لأنواع tuple في هذا المثال) ستذهب إلى أبعد من ذلك لتبسيط الأمور.

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

const colors: Colors = getColors();
const center: Vertex3D = getCenter();

// Doesn't work! Produces a homogenous array.
const circle1: OpCircle = [...colors, ...center, radius];

// Doesn't work; can't write this function today.
const circle2: OpCircle = concat(colors, center, radius);

// We need to do this today; it's quite painful with more complex tuple types.
const circle3: OpCircle = [colors[0], colors[1], center[0], center[1], center[2], radius];

نأمل أن تكون هذه الأمثلة مفيدة!

يمكنك بسهولة كتابة نوع Concat<> ، وإنشاء نوع Concat3<> ، باستخدام Concat<> .

ثم،

type OpCircle = Concat3<Colors, Vertex3D, [number] /* radius */>;

مما سبق ، يمكنك كتابة دالة concat مع أحمال زائدة لـ 2،3،4،5،6 ، إلخ. عدد الحجج.

من الممكن أيضًا كتابة Concat <> ضمنيًا يأخذ مجموعة tuple ويقوم بتجميع المجموعات. نوع var-arg Concat <>.


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

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

ربما لا يفعل الأشخاص الذين يكتبون الأنواع ما يكفي لتحسينها؟

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

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

يمكنك بسهولة كتابة نوع Concat<> ، وإنشاء نوع Concat3<> ، باستخدام Concat<> .

هل يمكنك تقديم تنفيذ للنوع Concat<> الذي تصفه؟ من السهل كتابة Cons<> ، لكن Concat<> ليس بهذه السهولة (بالنسبة لي) وأحب أن أرى ما تتخيله.

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

للتسلسل المنتظم لمجموعتي tuples ،
https://github.com/AnyhowStep/ts-trampoline-test (يستخدم الترامبولين لكونكات مجموعات كبيرة جدًا ، والتي لن يحتاجها معظم الناس)

سيكون Concat3 ، C>

سيكون VarArgConcat ،
VarArgConcat<TuplesT extends readonly (readonly unknown[])[], ResultT extends readonly unknown[] = []>

في حين أن tuple ليس فارغًا ، VargArgConcat<PopFront<TuplesT>, Concat<ResultT, TuplesT[0]>>

إذا كانت TuplesT فارغة ، فارجع إلى ResultT

بالطبع ، ستؤدي العودية الساذجة إلى حدوث أخطاء قصوى في العمق مع مجموعات من الطول المناسب. لذلك ، إما استخدام تقنية العودية في ts-toolbelt ، أو استخدام الترامبولين مع النسخ واللصق إلى العمق المطلوب


هذا الريبو الذي ربطته يستخدم Reverse<> لتنفيذ Concat<> . لقد قمت بنسخ الكود من مشروع آخر أعمل عليه.

أوافق على أن هذه ستكون ميزة مفيدة للغاية.

لنفترض أن لدينا نوع T :

type T = {
  tags: ["a", "b", "c"];
};

ونريد إنشاء نوع جديد ، بعلامة إضافية "d" ، مضافة إلى T["tags"] tuple. قد يحاول المستخدمون في البداية إنشاء هذه الأداة المساعدة ( WithTag<NewTag, ApplyTo> ) على النحو التالي:

type WithTag<
  Tag extends string,
  Target extends {tags: string[]}
> = Target & {
  tags: [Tag, ...Target["tags"]];
};

محاولة هذا حاليًا يؤدي إلى ظهور الخطأ A rest element type must be an array type . قد يعتقد المستخدمون أن تبديل string[] مقابل Array<string> يحدث فرقًا ، لكنه لا يحدث فرقًا. ولا استخدام شرط + never :

type WithTag<
  Tag extends string,
  Target extends {tags: string[]}
> = Target & {
- tags: [Tag, ...Target["tags"]];
+ tags: Target["tags"] extends string[] ? [Tag, ...Target["tags"]] : never;
};

ملعب الرابط: https://www.typescriptlang.org/play؟#code/C4TwDgpgBA6glsAFgFQIYHMA8AoKU3pQQAewEAdgCYDOU1wATnOegDS76oPoTBGkUaUAN7AM1AFx1GzdAG0AugF9sAPigBeTt15QAZCI5j0kqHIKsoAOhtodwOQCJj1RwoUBubEq -ZQkKABJTUM8FyknVEdLRwAjaKhHAGM3Lx9sP3BoZBD4JAJMR0oEwNVfAHoAKkrcSqgAUWJIJLJKKAADZHaoYAB7KFjoXoAzHsRoYd6AGynegHdZHqyrWqhV4VWe8QjHKJj4mJSY4s9VlShK8qA

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

لسوء الحظ ، لا تعمل هذه الاستراتيجية مع معايير الراحة. هذه تتحول إلى مصفوفات:

function i(a: number, b?: string, ...c: boolean[]): number {
}
let curried = curry(i, 12);
curried('foo', [true, false]);
curried([true, false]);

هنا ، كاري: ...([string, boolean[]] | [boolean[]]) => number .
أعتقد أن هذا يمكن دعمه إذا كانت هناك حالة خاصة للوظائف ذات معلمة tuple rest ، حيث يكون العنصر الأخير في المجموعة عبارة عن مصفوفة.
في هذه الحالة ، سيسمح استدعاء الوظيفة لوسائط إضافية من النوع الصحيح لمطابقة المصفوفة.
ومع ذلك ، يبدو هذا معقدًا جدًا بحيث لا يستحق العناء.

هذا له مشكلتان:

  1. الرمز غير صحيح. curried() لا يقبل مصفوفة. يمكن أن يتم ملء معلمة الراحة c بـ [true, false] بواسطة curried('foo', ...[true, false]) لكن هذا سيفشل في TypeScript مع هذا الاقتراح. قد لا نتمكن من تقديم حل للطباعة في بعض الحالات ، ولكن لا نشجع على تقديم خطأ لشخص ما!
  2. عن غير قصد ، قمت بدمج المعلمات الاختيارية والباقية ، وكشفت عن خطأ في اقتراحك. لا يمكن استدعاء curried() بدون b ولكن باستخدام c . القيام بذلك سيؤدي إلى سوء السلوك. يعرف TypeScript أن curried() هو (...items: [string, boolean[]] | [boolean[]]) لكن هذا ليس صحيحًا . نظرًا لأن JavaScript يكتب أقل ، فإن تمرير [true, false] إلى c (بافتراض أننا حللنا المشكلة أعلاه) مع curried([true, false]) لن يتم تعيين b إلى undefined (أو قيمته الافتراضية) و c إلى [true, false] ، ولكن سيتم تعيين b إلى true و c إلى [false] !

أقترح الإصلاحات التالية:

  1. بالنسبة للمشكلة الثانية (والأسهل) ، يكون الحل بسيطًا: لا تستنتج union tuple لآخر وسيطة اختيارية (على سبيل المثال [number, string, boolean[]] | [number, boolean[]] في حالتنا) عندما يكون هناك متغير راحة. بدلا من ذلك، يستنتج [number, string, boolean[]] | [number] - وهذا هو، حالة واحدة للتوقيع الكامل، بما في ذلك جميع اخليارات والراحة، واحد لكل اختياري عدا الأخير، واحد دون آخر وبقية العالم.
  2. المشكلة الأولى أكثر تعقيدًا: لقد قلت بالفعل أنك تعتقد أنها معقدة للغاية بحيث لا تستحق العناء. أعتقد أن الأمر يستحق العناء نظرًا لشعبية متغيرات الراحة ، لكنه _ ضروري_ بسبب المشكلة الأولى (النصر! 😄). أعتقد أنه سيكون من الجيد أن نكشف الواجهة عن مجموعة tuple-with-last-rest-array (أفكر في بناء الجملة [t1, t2, t3, ...arr] ) ، لكننا لسنا بحاجة إلى ذلك. يمكننا البقاء معه داخليًا (هاها ، لا يزال يتعين عليك التعامل مع كيفية عرض النوع في IDE 😈).

لكن بعد كل الشكاوى والاستفزازات ، اقتراح عظيم! شكرًا لك 👍 (فقط لتهدئتك ، هذه هي المشكلة الأولى على الإطلاق في GitHub التي رددت عليها بثلاثة رموز تعبيرية - 👍 و 🎉 و ❤️).

سيكون هذا مفيدًا حقًا في Angular Injector الذي يتعين عليه حاليًا استخدام any https://github.com/angular/angular/issues/37264

في هذا المثال ، يمكن تمثيل A و B و C كنوع عام متغير واحد ...A . لكن ليس لدي أي فكرة عن كيفية تعيين هذا إلى شيء حيث يتم تضمين كل عنصر من العناصر العامة المتغيرة في نوع آخر ( Type ). ربما مع نوع مساعد؟ أم يجب أن تسمح البنية بشيء مثل ...Type<A> ؟

export declare interface TypedFactoryProvider<T, A, B, C> {
    provide: Type<T | T[]> | InjectionToken<T | T[]>;
    multi?: boolean;
    useFactory: (a: A, b: B, c: C) => T;
    deps: [Type<A>, Type<B>, Type<C>];
}

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

عند الانتهاء من ذلك ، يرجى تذكر تحديث المعلمة الثانية لـ String.prototype.replace ، بحيث تحتوي أخيرًا على كتابة مناسبة في Typescript!

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_function_as_a_parameter

Griffork أنت تدرك بالفعل أن ذلك سيتطلب تحليل regex لمعرفة عدد مجموعات الالتقاط ، أليس كذلك؟

سيكون هذا مفيدًا حقًا في Angular Injector الذي يتعين عليه حاليًا استخدام any angular / angular # 37264

في هذا المثال ، يمكن تمثيل A و B و C كنوع عام متغير واحد ...A . لكن ليس لدي أي فكرة عن كيفية تعيين هذا إلى شيء حيث يتم تضمين كل عنصر من العناصر العامة المتغيرة في نوع آخر ( Type ). ربما مع نوع مساعد؟ أم يجب أن تسمح البنية بشيء مثل ...Type<A> ؟

export declare interface TypedFactoryProvider<T, A, B, C> {
  provide: Type<T | T[]> | InjectionToken<T | T[]>;
  multi?: boolean;
  useFactory: (a: A, b: B, c: C) => T;
  deps: [Type<A>, Type<B>, Type<C>];
}

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

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

أشعر أنه سيُكتب شيئًا كالتالي:

export declare interface TypedFactoryProvider<T, ...P> {
  provide: Type<T | T[]> | InjectionToken<T | T[]>;
  multi?: boolean;
  useFactory: (...providers: ...P) => T;
  deps: [...Type<P>];
}

تم إصلاح هذه المشكلة الآن بواسطة # 39094 ، المحدد لـ TS 4.0.

إذا كان هذا يأتي مع 4.0 ، فلدينا الآن سبب لتسميته 4.0 😃
هذه حقًا ميزة جديدة رئيسية

هذا عظيم! الشيء الوحيد "اليسار" هو نفسه لأنواع السلاسل الحرفية

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

كما في pipe(map<T, V>(...), map<V, U>(...), filter(...), ...) . كيف تكتبه بطريقة ليست ما يفعلونه الآن؟ (عشرات الأسطر ذات الأطوال المختلفة للطباعة)

gioragutt باستخدام العلاقات العامة التي قدمها @ ahejlsberg أعتقد أن هذا

type Last<T extends readonly unknown[]> = T extends readonly [...infer _, infer U] ? U : undefined;

interface UnaryFunction<T, R> { (source: T): R; }

type PipeParams<T, R extends unknown[]> = R extends readonly [infer U] ? [UnaryFunction<T, U>, ...PipeParams<R>] : [];

function pipe<T, R extends unknown[]>(...fns: PipeParams<T, R>): UnaryFunction<T, Last<R>>;

tylorr لا يعمل تمامًا ، بسبب خطأ في النوع الدائري.

ومع ذلك ، يعمل الحل المعتاد .

type Last<T extends readonly unknown[]> = T extends readonly [...infer _, infer U] ? U : undefined;

interface UnaryFunction<T, R> { (source: T): R; }

type PipeParams<T, R extends unknown[]> = {
    0: [],
    1: R extends readonly [infer U, ...infer V]
    ? [UnaryFunction<T, U>, ...PipeParams<U, V>]
    : never
}[R extends readonly [unknown] ? 1 : 0];

declare function pipe<T, R extends unknown[]>(...fns: PipeParams<T, R>): UnaryFunction<T, Last<R>>;

isiahmeadows يبدو أن هذا لا يعمل بالنسبة لي. 😢
مثال ملعب .

لقد اقتربت شيئًا ما من العمل ولكنه لن يستنتج الأنواع.
مثال الملعب

كان علي أن أتغير
R extends readonly [unknown] ? 1 : 0
إلى
R extends readonly [infer _, ...infer __] ? 1 : 0

غير متأكد من السبب

tylorrtreybrisbane قد يكون ذات الصلة: https://github.com/microsoft/TypeScript/pull/39094#issuecomment -645730082

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

تعد أنواع tuple المتنوعة إضافة رائعة للغة ، شكرًا لك على المجهود!

يبدو أن إنشاءات مثل curry قد تستفيد أيضًا (تم اختبارها للتو مع ملعب التدريج ):

// curry with max. three nestable curried function calls (extendable)
declare function curry<T extends unknown[], R>(fn: (...ts: T) => R):
  <U extends unknown[]>(...args: SubTuple<U, T>) => ((...ts: T) => R) extends ((...args: [...U, ...infer V]) => R) ?
    V["length"] extends 0 ? R :
    <W extends unknown[]>(...args: SubTuple<W, V>) => ((...ts: V) => R) extends ((...args: [...W, ...infer X]) => R) ?
      X["length"] extends 0 ? R :
      <Y extends unknown[]>(...args: SubTuple<Y, X>) => ((...ts: X) => R) extends ((...args: [...Y, ...infer Z]) => R) ?
        Z["length"] extends 0 ? R : never
        : never
      : never
    : never

type SubTuple<T extends unknown[], U extends unknown[]> = {
  [K in keyof T]: Extract<keyof U, K> extends never ?
  never :
  T[K] extends U[Extract<keyof U, K>] ?
  T[K]
  : never
}

type T1 = SubTuple<[string], [string, number]> // [string]
type T2 = SubTuple<[string, number], [string]> // [string, never]

const fn = (a1: number, a2: string, a3: boolean) => 42

const curried31 = curry(fn)(3)("dlsajf")(true) // number
const curried32 = curry(fn)(3, "dlsajf")(true) // number
const curried33 = curry(fn)(3, "dlsajf", true) // number
const curried34 = curry(fn)(3, "dlsajf", "foo!11") // error

لا تعمل الوظيفة العامة مع الكاري أعلاه.

لا أعتقد أن هذا العلاقات العامة يحل هذه المشكلة بالذات tbh.

مع العلاقات العامة هذا يعمل

function foo<T extends any[]>(a: [...T]) {
  console.log(a)
}

foo<[number, string]>([12, '13']);

لكن هذه المسألة تود أن ترى تطبيقًا لذلك بقدر ما أرى:

function bar<...T>(...b: ...T) {
  console.log(b)
}

bar<number, string>(12, '13');

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

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

declare function foo<T extends any[]>(...a: T): void;

foo(12, '13');  // Just have inference figure it out
foo<[number, string]>(12, '13');  // Expclitly, but no need to

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

تضمين التغريدة كنت أسأل لأن بعض المكتبات (RxJS كما هو مذكور) تستخدم الحلول لتوفير هذه الوظيفة. لكنها محدودة.

bar<T1>(t1: T1);
bar<T1, T2>(t1: T1, t2:T2);
bar<T1, T2, T3>(t1: T1, t2:T2, t3: T3, ...t: unknown) { ... }

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

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

foo<[number, string]>([12, '13']);

ليس من الواضح هنا أن tuple يشير إلى معامل الراحة هذا إذا نظرت إليه من الخارج

foo<[number, string]>(12, '13'); 

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

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

التناقضات البسيطة في بناء الجملة لا تستحق في الحقيقة تكلفة دعم منفصلة
طيب القلب. سيكون استخدام الأنواع قرار تصميم صحيحًا عندما كان TS يخطط
دعم معلمات الراحة ، لكنني أعتقد الآن أنه قد يؤدي إلى المزيد
الارتباك لكل من مطوري اللغة والمستخدمين. كنا بحاجة إلى حل ل
هذه المسألة ، وقد قام أندرس بعمله بشكل جيد للغاية في تجنب ذلك
التعقيد بالتمسك بـ [...T] بدلاً من T . القبعات!

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

في الجمعة ، 19 يونيو 2020 ، 10:41 كتب Győri Sándor [email protected] :

ahejlsberg https://github.com/ahejlsberg أرى. كنت أسأل بسبب
استخدمت بعض المكتبات (RxJS كما هو مذكور) الحلول لتوفير ذلك
وظائف. لكنها محدودة.

شريط(t1: T1) ؛ شريط(t1: T1، t2: T2) ؛ شريط(t1: T1، t2: T2، t3: T3، ... t: غير معروف) {...}

والآن إما يلتزمون بذلك ، أو يطلبون من المستخدمين كتابة الأقواس ،
وهي ليست بهذه البديهية.

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

foo <[number، string]> ([12، '13'])؛

ليس من الواضح هنا أن tuple يشير إلى معامل الراحة إذا
تنظر إليه من الخارج

foo <[number، string]> (12، '13') ؛

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

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

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

سيكون المفهوم الأكثر عمومية بالطبع هو التنفيذ الكامل للأنواع التابعة ، والتي يمكن من خلالها اشتقاق كل شيء آخر ، ولكن الذهاب إلى هذا الحد ليس ضروريًا:
كما أظهر C ++ ، وبدرجة أقل ، Rust ، تمنحك بعض المفاهيم المتسقة واسعة النطاق الكثير من الميزات مجانًا.
هذا مشابه لما قام به OCaml و Haskell (وأفترض F #؟) على مستوى القيمة ، فقط على مستوى النوع.

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

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

في الجمعة ، 19 يونيو 2020 ، 12:47 كتب Bennett Piater [email protected] :

أنا بالطبع لست قريبًا من مكانته ، لكنني أختلف بكل احترام
مع ahejlsberg https://github.com/ahejlsberg .
في تجربتي ، يأتي الكثير من تعقيد الطباعة المطبوعة منحقيقة أن الكثير من الميزات (مثيرة للاهتمام ومفيدة بالتأكيد)بغلاف خاص كمفاهيمهم الخاصة.

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

سيكون المفهوم الأكثر عمومية بالطبع هو التنفيذ الكامل
الأنواع التابعة ، والتي يمكن من خلالها اشتقاق كل شيء آخر ، ولكن
الذهاب إلى هذا الحد ليس ضروريًا:
كما أظهر C ++ ، وبدرجة أقل ، الصدأ ، عدد قليل من النطاقات الكبيرة ،
تمنحك المفاهيم المتسقة الكثير من الميزات مجانًا.
هذا مشابه لما قام به OCaml و Haskell (وأفترض F #؟)
مستوى القيمة ، فقط على مستوى النوع.

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

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

@ polkovnikov-ph يسعدني أننا نتفق على المشكلة المطروحة :)

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

كل هذا خارج الموضوع عن هذا الموضوع وتجري مناقشته هنا . لذلك سأحاول أن أكون صريحًا:

استغرق الأمر عقودًا من الأكاديميين لاكتشاف النهج الصحيح للتصنيف الفرعي: تم إنشاء نظام من نوع mlsub منذ 6 سنوات فقط ، بعد إصدار TypeScript لأول مرة. يمكن أن يكون هذا الأساس للفئات والواجهات وأنواع الاتحاد والتقاطع ذات المفاهيم الشاملة.

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

وبخلاف ذلك ، سيتعين على "نظام الكتابة المصمم بعناية" إزالة الأنواع الشرطية من اللغة ، ولا أعتقد أن أي شخص على مستوى مهمة تحويل 60٪ من DefinitelyTyped لاستخدام أي بديل يتم اختياره لاستبدالها. (ثم ​​قم بذلك عدة مرات ، لأنها ليست المشكلة الوحيدة.)

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

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