Typescript: اقتراح: احصل على نوع أي تعبير باستخدام typeof

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

تنفيذ العمل لهذا الاقتراح

جربه: npm install yortus-typescript-typeof

عرض الفرق: هنا .

سيناريو المشكلة

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

لدي مجموعة مكتوبة بشدة ولكن نوع العنصر مجهول / غير معروف ، كيف يمكنني الإشارة إلى نوع العنصر؟ (# 3749)
// Mapping to a complex anonymous type. How to reference the element type?
var data = [1, 2, 3].map(v => ({ raw: v, square: v * v }));
function checkItem(item: typeof data[0] /* ERROR */) {...}

// A statically-typed dictionary. How to reference a property type?
var things = { 'thing-1': 'baz', 'thing-2': 42, ... };
type Thing2Type = typeof things['thing-2']; // ERROR

// A strongly-typed collection with special indexer syntax. How to reference the element type?
var nodes = document.getElementsByTagName('li');
type ItemType = typeof nodes.item(0); // ERROR
تقوم الدالة بإرجاع نوع محلي / مجهول / لا يمكن الوصول إليه ، كيف يمكنني الإشارة إلى نوع الإرجاع هذا؟ (# 4233 ، # 6179 ، # 6239)
// A factory function that returns an instance of a local class
function myAPIFactory($http: HttpSvc, id: number) {
    class MyAPI {
        constructor(http) {...}
        foo() {...}
        bar() {...}
        static id = id;
    }
    return new MyAPI($http);
}
function augmentAPI(api: MyAPI /* ERROR */) {...}
لدي واجهة ذات شكل مجهول معقد ، كيف يمكنني الإشارة إلى أنواع خصائصها وخصائصها الفرعية؟ (# 4555 ، # 4640)
// Declare an interface DRY-ly and without introducing extra type names
interface MyInterface {
    prop1: {
        big: {
            complex: {
                anonymous: { type: {} }
            }
        }
    },

    // prop2 shares some structure with prop1
    prop2: typeof MyInterface.prop1.big.complex; // ERROR
}

لماذا نحتاج إلى الإشارة إلى الأنواع المجهولة / المستنبطة؟

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

الحلول الحالية

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

let dummyReturnVal = null && someFunction(0, ''); // NB: someFunction is never called!
let ReturnType = typeof dummyReturnVal;           // Now we have a reference to the return type

يحتوي هذا الحل البديل على بعض العيوب:

  • من الواضح جدًا أنه خلل ، غير واضح للقراء
  • تلوث المعرف (يجب إدخال متغير مثل dummyReturnValue )
  • لا يعمل في السياقات المحيطة ، لأنه يتطلب بيانًا حتميًا

الحل المقترح

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

السماح للمعامل typeof أن يكون تعبيرًا عشوائيًا. هذا مسموح به بالفعل لـ typeof expr في موضع ذو قيمة مثل if (typeof foo() === 'string') . لكن هذا الاقتراح يسمح أيضًا بتعبير تعسفي عند استخدام typeof في موضع نوع كاستعلام نوع ، على سبيل المثال type ElemType = typeof list[0] .

يتوافق هذا الاقتراح بالفعل بشكل وثيق مع الصياغة الحالية للمواصفات:

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

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

النحو والدلالات

الدلالات هي بالضبط كما هو مذكور بالفعل في المواصفات 4.18.6 :

يأخذ عامل التشغيل "typeof" معاملًا من أي نوع وينتج قيمة من النوع البدائي للسلسلة. في المواضع التي يُتوقع فيها نوع ما ، يمكن أيضًا استخدام "typeof" في استعلام نوع (القسم 3.8.10) لإنتاج نوع التعبير.

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

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

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

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

أمثلة

// Mapping to a complex anonymous type. How to reference the element type?
var data = [1, 2, 3].map(v => ({ raw: v, square: v * v }));
function checkItem(item: typeof data[0]) {...} // OK: item type is {raw:number, square:number}

// A statically-typed dictionary. How to reference a property type?
var things = { 'thing-1': 'baz', 'thing-2': 42, ... };
type Thing2Type = typeof things['thing-2']; // OK: Thing2Type is number

// A strongly-typed collection with special indexer syntax. How to reference the element type?
var nodes = document.getElementsByTagName('li');
type ItemType = typeof nodes.item(0); // OK: ItemType is HTMLLIElement

// A factory function that returns an instance of a local class
function myAPIFactory($http: HttpSvc, id: number) {
    class MyAPI {
        constructor(http) {...}
        foo() {...}
        bar() {...}
        static id = id;
    }
    return new MyAPI($http);
}
type MyAPI = typeof myAPIFactory(null, 0); // OK: MyAPI is myAPIFactory's return type
function augmentAPI(api: MyAPI) {...} // OK

// Declare an interface DRY-ly and without introducing extra type names
interface MyInterface {
    prop1: {
        big: {
            complex: {
                anonymous: { type: {} }
            }
        }
    },

    // prop2 shares some structure with prop1
    prop2: typeof (<MyInterface>null).prop1.big.complex; // OK: prop2 type is {anonymous: {type: {}}}
}

مناقشة إيجابيات / سلبيات

المعارضون : جماليات بناء الجملة الضعيفة. تم اقتراح التركيبات البديلة التي تعالج الحالات الفردية في # 6179 و # 6239 و # 4555 و # 4640.

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

ضد : التعبير في موضع الكتابة أمر محير.

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

ضد : يمكن إساءة استخدام هذا لكتابة استعلامات ضخمة طويلة من نوع متعدد الأسطر.

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

تأثير التصميم والأسئلة والعمل الإضافي

التوافق

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

أداء

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

الأدوات

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

قد تظهر التعبيرات المعقدة في ملفات .d.ts

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

يتم التعامل مع الأنواع العودية بقوة

يبدو أن المترجم لديه بالفعل كل المنطق اللازم للتعامل مع أشياء مثل هذا:

function foo<X,Y>(x: X, y: Y) {
    var result: typeof foo(x, y); // ERROR: 'result' is referenced in its own type annotation
    return result;
}
يمكن الاستعلام عن نوع إرجاع دالة محملة بشكل زائد

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

declare function foo(a: boolean): string;
declare function foo(a: number): any[];
type P = typeof foo(0);    // P is any[]
type Q = typeof foo(true); // Q is string
Suggestion Too Complex

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

تم فتح العلاقات العامة على 17961 #.

ال 157 كومينتر

لأي شخص يريد طريقة سريعة للعب بهذا في VS Code مع intellisense وما إلى ذلك ، إليك مستودع ألعاب .

اكتب P = typeof foo (0) ؛ // P هو أي []
اكتب Q = typeof foo (صحيح) ؛ // Q عبارة عن سلسلة

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

type P = typeof foo(number);    // P is any[]
type Q = typeof foo(boolean); // Q is string

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

tinganho بالضبط!
على الرغم من أنه لا يزال بإمكاننا كتابة typeof foo("abc") مع # 5185
هنا "abc" هو نوع السلسلة المفردة

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

كنت أفكر ، ماذا لو كانت هناك طريقة مختصرة جدًا لكتابة شيء مثل بناء الجملة الخاص بك foo(number) ولكن باستخدام التعبير الحالي ميكانيكا التحليل؟ لذا ، كتجربة ، قدمت تعبيرًا جديدًا: _ القمر _ as_. يمكنك فقط كتابة as T وهذا اختصار لـ (null as T) . أنت تقول في الأساس ، "أنا لا أهتم بالقيمة ولكني أريد أن يكون التعبير من النوع X'_.

من خلال هذا التغيير (الذي قمت بتطبيقه في playground repo ) ، يمكنك كتابة شيء أقرب كثيرًا إلى الصيغة المقترحة ، لكنه لا يزال محللًا كتعبير عادي:

type P = typeof foo(as number);    // P is any[]
type Q = typeof foo(as boolean); // Q is string

let prop2: typeof (as MyInterface).prop1.big.complex; // prop2 type is {anonymous: {type: {}}}

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

type P = typeof foo(<number>);    // P is any[]
type Q = typeof foo(<boolean>); // Q is string

let prop2: typeof (<MyInterface>).prop1.big.complex; // prop2 type is {anonymous: {type: {}}}

قد يُطلق على الصيغة الثانية تأكيد النوع _nullary_ ، حيث يكون التعبير <T> اختصارًا لـ (<T> null) .

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

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

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

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

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

ستكون الخطة الأساسية للهجوم هي:

  • قم بتوسيع القواعد النحوية عند تحليل typeof للسماح للأشياء التي تشبه استدعاءات الوظائف ، والوصول إلى الممتلكات ، والوصول إلى الخصائص المفهرسة
  • عند تحليل وسيطات استدعاء دالة في استعلام نوع (دعنا نسميها _psuedocalls_ / _psuedoarguments_) ، استخدم الدالة parseType . سيؤدي هذا إلى إنشاء TypeNode ، ولكن قم بتعيين علامة على العقدة تشير إلى أنها كانت نوعًا تم تحليله في سياق استعلام نوع
  • في المدقق ، يتحقق checkExpression من هذه العلامة ويستدعي getTypeFromTypeNode بدلاً من معالجة التعبير العادي

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


تدوين المفهرس
var data = [1, 2, 3];
const LOBOUND = 0;
type Elem1 = typeof data[0];        // number
type Elem2 = typeof data[999999];   // number or ERROR?
type Elem3 = typeof data[1+2];      // ERROR or number?
type Elem4 = typeof data[LOBOUND];  // ERROR or number?

var tuple: [number, string] = [123, 'abc'];
type Elem4 = typeof tuple[0];       // number or number|string?
type Elem5 = typeof tuple[1];       // string or number|string?
type Elem6 = typeof tuple[999999];  // number|string or ERROR?

const ABC: 'a-b-c' = 'a-b-c';
let dict = { 'a-b-c': 123, 'd-e-f': true };
type Prop1 = typeof dict['a-b-c'];  // number
type Prop2 = typeof dict['d-e-f'];  // boolean
type Prop3 = typeof dict[ABC];      // ERROR or number or any?

تدوين نوع إرجاع الدالة
// A simple function
declare function f1(n: number): string[];
type Ret1 = typeof f1(number);      // string[]
type Ret2 = typeof f1(0);           // ERROR or string[]?

// An asynchronous function that either accepts a callback or returns a Promise
declare function f2(n: number): Promise<string[]>;
declare function f2(n: number, cb: (err?: any, result?: string[]) => void): void;
type Ret3 = typeof f2(number);                    // Promise<string[]>
type Ret4 = typeof f2(number, any);               // void
type Ret5 = typeof f2(number, Function);          // ERROR: Function not assignable to callback
type Ret6 = typeof f2(number, (err: any, result: string[]) => void); // void
type Ret7 = typeof f2(number, (...args) => any);  // void

// A special function-like object
interface Receiver {
    (data: string[]): void;
    transmogrify(): number[];
}
declare function f3(n: number, receiver: Receiver): Promise<void>;
declare function f3(n: number, callback: (err?: any, result?: string[]) => void): void;
type Ret8 = typeof f3(number, Receiver);           // Promise<void>
type Ret9 = typeof f3(number, any);                // ambiguous? or picks first overload?
type Ret10 = typeof f3(number, Function);          // ERROR
type Ret11 = typeof f3(number, (...args) => any);  // void since not assignable to Receiver

// A function with parameter destructuring
interface CartesianCoordinate {/***/}
interface PolarCoordinate {/***/}
declare function f4({ x: number, y: number }): CartesianCoordinate;
declare function f4({ r: number, t: number }): PolarCoordinate;
type Ret12 = typeof f4(any);        // ambiguous? or picks first overload?
type Ret13 = typeof f4({x;y});      // CartesianCoordinate
type Ret14 = typeof f4({r;t});      // PolarCoordinate
type Ret15 = typeof f4({x;r;t;y});  // ambiguous? or picks first overload?

// Type-ception: is there anything wrong with typeof-in-typeof?
declare function f5(n: number, receiver: Receiver): Promise<void>;
declare function f5(n: number, callback: (err?: any, result?: string[]) => void): void;
function myCallback(err, result) {/***/}
var myReceiver: Receiver;
type Ret16 = typeof f5(number, typeof myReceiver); // Promise<void>
type Ret17 = typeof f5(number, typeof myCallback); // void


استخراج جزء من فئة / نوع واجهة

وهذا هو ، أمثلة typeof (<MyInterface> null).prop1.big.complex; أعلاه.

أعتبر من التعليقات أعلاه أن هذا خارج النطاق ولن يتم دعمه. هل هذا صحيح؟

تدوين المفهرس
var data = [1, 2, 3];
const LOBOUND = 0;
type Elem1 = typeof data[0];        // number
type Elem2 = typeof data[999999];   // number
type Elem3 = typeof data[1+2];      // ERROR, only literals allowed here
type Elem4 = typeof data[LOBOUND];  // number when const resolution is done, otherwise any

var tuple: [number, string] = [123, 'abc'];
type Elem4 = typeof tuple[0];       // number
type Elem5 = typeof tuple[1];       // string 
type Elem6 = typeof tuple[999999];  // number|string

const ABC: 'a-b-c' = 'a-b-c';
let dict = { 'a-b-c': 123, 'd-e-f': true };
type Prop1 = typeof dict['a-b-c'];  // number
type Prop2 = typeof dict['d-e-f'];  // boolean
type Prop3 = typeof dict[ABC];      // number when const resolution work is done, otherwise any

تدوين نوع إرجاع الدالة
// A simple function
declare function f1(n: number): string[];
type Ret1 = typeof f1(number);      // string[]
type Ret2 = typeof f1(0);           // error, 0 is not a type

// An asynchronous function that either accepts a callback or returns a Promise
declare function f2(n: number): Promise<string[]>;
declare function f2(n: number, cb: (err?: any, result?: string[]) => void): void;
type Ret3 = typeof f2(number);                    // Promise<string[]>
type Ret4 = typeof f2(number, any);               // void
type Ret5 = typeof f2(number, Function);          // ERROR: Function not assignable to callback
type Ret6 = typeof f2(number, (err: any, result: string[]) => void); // void
type Ret7 = typeof f2(number, (...args) => any);  // void

// A special function-like object
interface Receiver {
    (data: string[]): void;
    transmogrify(): number[];
}
declare function f3(n: number, receiver: Receiver): Promise<void>;
declare function f3(n: number, callback: (err?: any, result?: string[]) => void): void;
type Ret8 = typeof f3(number, Receiver);           // Promise<void>
type Ret9 = typeof f3(number, any);                // picks first overload
type Ret10 = typeof f3(number, Function);          // ERROR
type Ret11 = typeof f3(number, (...args) => any);  // void since not assignable to Receiver

// A function with parameter destructuring
interface CartesianCoordinate {/***/}
interface PolarCoordinate {/***/}
declare function f4({ x: number, y: number }): CartesianCoordinate;
declare function f4({ r: number, t: number }): PolarCoordinate;
type Ret12 = typeof f4(any);        // picks first overload
type Ret13 = typeof f4({x;y});      // CartesianCoordinate
type Ret14 = typeof f4({r;t});      // PolarCoordinate
type Ret15 = typeof f4({x;r;t;y});  // picks first overload

// Type-ception: is there anything wrong with typeof-in-typeof?
declare function f5(n: number, receiver: Receiver): Promise<void>;
declare function f5(n: number, callback: (err?: any, result?: string[]) => void): void;
function myCallback(err, result) {/***/}
var myReceiver: Receiver;
type Ret16 = typeof f5(number, typeof myReceiver); // Promise<void>
type Ret17 = typeof f5(number, typeof myCallback); // void

أشعر بالفضول لما يحدث هنا:

const number = "number";
type Ret3 = typeof f2(number); // What happens here?

SaschaNaz سؤال جيد. وضع مماثل:

class MyClass { foo; bar; }
declare function f(inst: MyClass): number;
type Ret = typeof f(MyClass);     // number (presumably)

في هذه الحالة ، من المنطقي استخدام typeof f(MyClass) # $ لنوع MyClass _type_ قبل MyClass _value_ (أي دالة المُنشئ). الأول يؤدي إلى Ret = number ، أما الأخير فسيؤدي إلى شيء مثل error: MyClass is not a type .

هل ينطبق نفس المنطق على الاسم الذي يشير إلى كل من قيمة _type_ و _const؟ في المثال الخاص بك ، قد يعني ذلك أن النوع number سيكون له الأولوية دائمًا على القيمة الثابتة number . أي أفكارRyanCavanaugh؟

حسنًا ، سنحل هذا الأمر في ظل الدلالات المعتادة لتعبير نوع (كما لو كنت قد كتبت var x: [whatever] ). لذلك يمكن أن يكون لديك typeof f(MyClass) للإشارة إلى استدعاء f مع جانب المثيل ، و typeof f(typeof MyClass) يشير إلى استدعاء f مع وظيفة المُنشئ.

إذن ، يشير مثال SaschaNaz بشكل لا لبس فيه إلى number كنوع _ ، وليس كقيمة _const ، أليس كذلك؟

const number = "number";
type Ret3 = typeof f2(number); // Promise<string[]>

RyanCavanaugh هل يمكنك تأكيد أن المجموعة الثالثة من حالات الاستخدام خارج نطاقها؟ على سبيل المثال من OP:

// Declare an interface DRY-ly and without introducing extra type names
interface MyInterface {
    prop1: {
        big: {
            complex: {
                anonymous: { type: {} }
            }
        }
    },

    // prop2 shares some structure with prop1
    prop2: typeof (<MyInterface>null).prop1.big.complex; // OK: prop2 type is {anonymous: {type: {}}}
}

لن يتم دعم حالة الاستخدام هذه (بأي بناء جملة) في الوقت الحالي ، فهل هذا صحيح؟

أعتقد أنه يجب تغطية هذا من خلال السماح بتعبيرات this .

   prop2: typeof this.prop1.big.complex;

أعتقد أن الشيء const يجب حله بواسطة typeof آخر.

type Ret3 = typeof f2(typeof number); // typeof number is string so error here

... بينما سيؤدي هذا إلى حظر typeof data[LOBOUND] .

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

class MyClass {
    prop1: {
        big: {
            complex: {
                anonymous: { type: {} }
            }
        }
    };

    prop2: typeof this.prop1.big.complex; // prop2 type is {anonymous: {type: {}}}
}

interface MyInterface {
    prop1: {
        big: {
            complex: {
                anonymous: { type: {} }
            }
        }
    };

    prop2: typeof this.prop1.big.complex; // prop2 type is any
}

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

أريد أن أجعل نقطتين حول # 6179 و Angular.

// A factory function that returns an instance of a local class
function myAPIFactory($http: HttpSvc, id: number) {
    class MyAPI {
        constructor(token: string) {...}
        foo() {...}
        bar() {...}
        static id = id;
    }
    return MyAPI;
}
type MyAPIConstructor = typeof myAPIFactory(null, 0); // OK: MyAPI is myAPIFactory's return type
function augmentAPI(api: MyAPIConstructor) {...} // OK
  1. يمكن أن يكون عدد المعلمات كبيرًا. لنفترض 15 معلمة. في الوقت نفسه ، لا توجد أحمال زائدة ، والحمل الزائد فقط هو ما تحتاج إليه المعلمات في typeof . إذن في هذه الحالة ، هل يمكننا التفكير في بناء جملة مثل التالي؟

    type MyAPI = typeof myAPIFactory(...);
    
  2. لا يتم عادةً تعيين وظيفة المصنع لمتغير عام خاص به. يتم استخدام تعبير دالة:

    angular.module('app').factory('MyAPI', function($http: HttpSvc, id: number) { /*...*/ });
    

    هذا ما يبدو عليه عادة. كما يتضح ، لا يمكن استخدام typeof هنا على الإطلاق.

@ thron0 حدسي هو أن الشيء الذي يحتوي على أكثر من عدد قليل من المعلمات ولكن _أيضًا_إرجاع نوع مجهول سيكون نادرًا جدًا. ما رأيك؟

سيصبح نادرًا عندما تصبح Angular 1 نادرة ، وليس قبل ذلك.

بشكل أساسي ، ما وصفته هو ما هو نموذجي لمصنع الزاوي:

الشيء الذي يحتوي على أكثر من عدد قليل من المعلمات ولكنه يعرض أيضًا نوعًا مجهولاً

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

angular.module('app').factory('MyAPI', function($http: HttpSvc, id: number) {
    class MyAPI {
        constructor(token: string) {...}
        foo() {...}
        bar() {...}
        static id = id;
    }
    return MyAPI;
});

هذا ما يبدو عليه الحل البديل الحالي لهذا الموقف:

class MyAPI {
    // dependencies (1)
    protected $http: HttpSvc;
    protected id: number;

    constructor(token: string) {...}
    foo() {...}
    bar() {...}
    // Static properties cannot be used with this approach because:
    // 1) this class is a global variable so it can be shared by different 
    // instances of the DI container,
    // 2) the id property isn't available here as it's initialized in the subclass
    //// static id0 = id;
}

angular.module('app').factory('MyAPI', function($http: HttpSvc, id: number) { // (2)
    return class extends MyAPI {
        $http = $http; // (3)
        id = id;
    };
});

// this type is needed for type annotations in the services that require MyAPI as a dependency
type MyAPIConstructor = typeof MyAPI;

angular.module('app').factory('someOtherService', function(MyAPI: MyAPIConstructor) {
    var api = new MyAPI('qwerty');
    /* ... */
});

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

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

دعنا نتخيل امتداد بناء الجملة:

type          ::=  ... |  literalType | type typeAccess | guarded
guarded    ::= "type" id
literalType   ::= stringLiteral | numberLiteral | symLiteral | booleanLiteral 
typeAccess    ::= typeField | typeSubscript | typeCall
typeField     ::= "." id
typeSubscript ::= "[" type "]"
typeCall      ::= "(" [type {"," type}] ")"

يمكن ربط كل معرّف في النطاق في وقت واحد بالكيانين:

  • تجميع نوع الوقت
  • قيمة وقت التشغيل

نوع الحارس مقتطفات فقط السابق. هكذا

class A {}
var a: A;

يعادل

class A {}
var a: type A;

في هذه الحالة إذا كان لدينا فصل

class ABC {
    a: number;
    b(x: number): string;
    c:string[];
    d:[number, string];
}

ثم يمكننا الكتابة

var a: (type ABC).a; // number
var b: (type ABC).b(number); // string
var c: (type ABC).c[number]; // string
var d: (type ABC).c[0]; // number

ويغطي أيضًا دمج كيانات _type_ و _value_ ضمن نفس المعرف في النطاق المعجمي.

interface SAME {
    x: number;
}
namespace SAME: {
    export type x = boolean;
    export var x: string;
}

var a: SAME.x   // boolean
var b: (type SAME).x  // number
var b: (typeof SAME).x  // string

RyanCavanaugh ، هل يمكنك دراسة هذه الفكرة؟

كيف يمكنك التقاط نوع التعبير مع اقتراحك؟

في الخميس ، 19 مايو 2016 12:26 كتب أناتولي ريسين ، [email protected] :

مرحبًا ، أريد أن أقترح ترك نوع بناء الجملة / الدلالات سليمة و
بدلاً من ذلك ، نفذ نوعًا بسيطًا من الجبر:

دعنا نتخيل امتداد بناء الجملة:

النوع :: = ... | النوع الحرفي | اكتب typeAccess | حراسة
حراسة :: = "اكتب" معرف
literalType :: = stringLiteral | numberLiteral | رمزي | منطقي
typeAccess :: = typeField | النوع اكتب
typeField :: = "." هوية شخصية
typeSubscript :: = "[" type "]"
typeCall :: = "(" [type {"،" type}] ")"

يمكن ربط كل معرّف في النطاق في نفس الوقت بالاثنين
جهات:

  • تجميع نوع الوقت
  • قيمة وقت التشغيل

يقوم حارس _type_ باستخراج السابق فقط. هكذا

فئة أ {}
أ: أ

يعادل

فئة أ {}
أ: النوع أ

في هذه الحالة إذا كان لدينا فصل

فئة ABC {
رقم؛
ب (س: رقم): سلسلة ؛
سلسلة ج[]؛
د: [رقم ، سلسلة] ؛
}

ثم يمكننا الكتابة

var a: (type ABC) .a ؛ // numbervar b: (type ABC) .b (number) ؛ // stringvar c: (type ABC) .c [number] ؛ // stringvar d: (type ABC) .c [0] ؛ // رقم

ويغطي أيضًا دمج كيانات _type_ و _value_ ضمن كيانات
نفس المعرف في النطاق المعجمي.

الواجهة نفسها {
x: رقم ؛
} مساحة الاسم SAME: {
نوع التصدير x = منطقي ؛
تصدير var x: string ؛
}
var a: SAME.x // booleanvar b: (type SAME) .x // numbervar b: (typeof SAME) .x // string

RyanCavanaugh https://github.com/RyanCavanaugh،sandersn
https://github.com/sandersn هل يمكنك فحص هذه الفكرة؟

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

يذكرني المثال الخاص بك بأن نوع التعبير المنقط ونوع خاصية النوع # 1295 هو اقتراح مشابه تمامًا. ما عدا أحدهما أكثر ثباتًا والآخر أكثر ديناميكية.

أحب فكرة تخطي typeof الكلمات الرئيسية أيضًا.

   prop2: this.prop1.big.complex;

mhegazy في مثالك أنت تستخدم typeof في this-expression :

prop2: typeof this.prop1.big.complex;

بما أنه يمكننا القيام بذلك اليوم:

someProp: this;

في رأيي ، استقراء بناء الجملة أعلاه لمزيد من الخصائص المنقطة هو:

someProp: this.prop1.prop2.prop3;

و لا:

someProp: typeof this.prop1.prop2.prop3;

ولمجرد الاستمرار في الاستقراء - لماذا لا نتخطى نوع الكل معًا؟

someProps: foo(number)

لماذا لا نتخطى نوع الكل معا؟

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

class C {}

// Does 'x' have the instance type of 'C',
// or does it have the shape of its constructor?
let x: C;

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

سيكون من الجيد أن تكتب شيئًا كهذا:

interface Foo {
  bar: string;
}

interface Box<T> {
  container: T;
}

interface FooWithBoxedProps {
  bar: Box<Foo.bar>; // OR: bar: Box<typeof Foo.bar>;
}

والتي ستترجم فقط إلى هذا:

interface Foo {
  bar: string;
}

interface Box<T> {
  container: T;
}

interface FooWithBoxedProps {
  bar: Box<string>;
}

حسنًا ، يمكن الآن التعبير عن بعض السيناريوهات المذكورة سابقًا باستخدام أنواع الوصول المفهرسة _ (# 11929). ما عليك سوى دمج هذه الميزة مع تعبير typeof القياسي ويمكنك الحصول على شيء مثل هذا:

interface MyInterface {
    prop1: {
        big: {
            complex: {
                anonymous: { type: {} }
            }
        }
    },

    // prop2 shares some structure with prop1
    prop2: MyInterface["prop1"]["big"]["complex"];
}

ويعمل! (حاليا في مطبوعة @ التالي)

أعتقد أن الشيء الطبيعي التالي هو أن يكون لديك شيء مشابه ولكن بالنسبة للوظائف ، بعض "أنواع استدعاء الوظائف" مثل هذا:

interface FunctionLike{
    (arg1 : number, arg2 : string) : {a : number; b : string;}
}

type ReturnType = FunctionLike(number, string); // {a : number; b : string;} 

لذلك يمكننا دمجها بشكل طبيعي مع عامل التشغيل typeof

interface BigThing {
    testString: string;
    testNumber: number;
    testBoolean: boolean;
}

function getSmallThing(thing:BigThing){
    return {
        testString : thing.testString,
        testBoolean : thing.testBoolean
    }
}

type SmallThing = typeof getSmallThing(BigThing) // {testString: string; testBoolean : boolean}

ويبدو أن هذا هو بناء الجملة الذي تم اقتراحه بالفعل في بداية هذا الموضوع (tinganho). :)
ولكن ستكون ميزة عامة أكثر يتم إنشاؤها بشكل جيد باستخدام عامل التشغيل الحالي typeof ، تمامًا مثل أنواع الوصول المفهرسة_ التي تقوم بها الآن.

ومع ذلك ، لا تزال هناك بعض الأسئلة / الشكوك:

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

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

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

اكتب P = typeof foo (0) ؛ // P هو أي []
اكتب Q = typeof foo (صحيح) ؛ // Q عبارة عن سلسلة

كتبت هذه المناقشة حول السماح بالإشارة إلى المعلمات من خلال أنواعها ، أود أن أقترح السماح بنفس الشيء للوظائف الفعلية أيضًا.
السياق: أود كتابة دالة إلى map فوق العناصر:
function map<T, F extends Function>(fn: F, obj: T): { [P in keyof T]: typeof F(T[P]) }
السبب الذي يجعلني أرغب في الإشارة إلى الوظيفة بنوعها F (بصرف النظر عن اسمها fn ) سيكون للحالات التي قد لا يتوفر فيها اسمها ، على سبيل المثال I ' أود أن أكون قادرًا على قول type MapFn<T, F extends Function> = { [P in keyof T]: typeof F(T[P]) } .

الشيء الذي من المحتمل أن يكون أسهل في التنفيذ (ولا يزال مفيدًا جدًا) سيكون:

const myFunc = () => ({ x: 10 });
type myType = returnof myFunc;    // { x: number; }

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

تحرير: أدركت للتو أن هذا تم ذكره أعلاه بواسطة mpawelski 😄

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

JsonFreeman ألا يمكن أن تفرط في تحميل الوظائف فقط OR لأنواع الإرجاع المختلفة؟ ويمكن أن تتطلب الوظائف العامة تحديد النوع؟

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

آمل حقًا أن يتم تنفيذ هذا قريبًا لأنه يمثل ألمًا حقيقيًا.

الحل

نظرًا لأن الحل الأخير لا يعمل بعد الآن ، فأنا أستخدم هذا حاليًا:

// 0 argument function
export default function returnof<T>(fn: () => T): T;

// 1 argument function
// If no ambiguity simply infer the return type
export default function returnof<A, T>(fn: (a: A) => T): T;

// 1 argument function, with possible overload for the argument type.
// Explicitly set the type and use the correct overload
export default function returnof<A, T>(fn: (a: A) => T, a: A): T;

// ...

لا تحتاج إلى تحديد الحجج إذا لم يكن هناك حمل زائد لوظيفتك.

const hello = (arg: number) => 'World'

const helloReturnValue = returnof(hello)
type helloReturnType = typeof helloReturnValue // string

وظائف مثقلة

declare function hello(): void;
declare function hello(a: number): number;

const helloReturnValue = returnof(hello)
type helloReturnType = typeof helloReturnValue // void

const helloReturnValue = returnof(hello, 42)
type helloReturnType = typeof helloReturnValue // number

جميع التعاريف هنا:
https://github.com/kube/returnof/blob/master/lib/returnof.d.ts

لقد قمت للتو بإنشاء حزمة NPM صغيرة لتجميعها بسهولة دون تلويث مشروعك.

إذا تمت إضافة Generics الاختيارية كما هو مقترح في https://github.com/Microsoft/TypeScript/issues/2175 ، فستسمح بحل تصريحي بسيط فقط:

type Return<T extends () => S, S> = S

واستخدمه على هذا النحو:

type helloReturnType = Return<typeof hello>

mhegazyJsonFreeman هل من خطة لهذه الميزة؟

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

function createData(){
  return <=MyData>{
    dataProp1: <string>null
  }
}

function method1(){
  let self: MyComponent = this;
  console.log(self.dataProp1);
  self.method2();
}

function method2(){
  let self: MyComponent = this;
  //dostuff
}

type MyComponent = MyData & MyMethods;

let componentOptions = {
  data: createData,
  methods: <=MyMethods>{method1, method2}
}
//todo: register component...

لاحظ أن الدالة createData يمكن أيضًا كتابتها

function createData(): =MyData {
  return {
    dataProp1: <string>null
  }
}

لقد وجدت عملًا ممتعًا حقًا يعمم على أي تعبير:

const varWithRightType = (false as true) && some.deep.access()
type MyType = typeof varWithRightType;

تعديل: توقف عن الضحك في وجهي ، فهذا نوع خطير

johnfn يبدو هذا ممتعًا للحالات البسيطة :) ، ولكن إذا كانت وظيفتك تتطلب بعض المعلمات ، فيجب عليك القيام بعمل إضافي مثل هذا:

const stateProps = (false as true) && mapStateToProps({} as any);

أنا أستخدم عملًا مختلفًا ، وهذا مفيد بشكل خاص لـ React & Redux ، عند محاولة استخدام استدلال النوع للحصول على نوع props من دالة mapStateToProps . ثم ليست هناك حاجة بعد الآن للإعلان يدويًا عن واجهة Props التي تم حقنها بواسطة Redux connect والمحافظة عليها ، وهي عملية شاقة للحفاظ عليها وعرضة للخطأ.
سيعالج هذا العمل أيضًا بشكل جيد أي توقيع دالة معين لحالات الاستخدام الأخرى.

مثال لحالة استخدام عامل التشغيل typeof "React & Redux":

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

import { returntypeof } from 'react-redux-typescript';
import { RootState } from '../../store';
...

const mapStateToProps = (state: RootState) => ({
  currencies: CurrencyRatesSelectors.getCurrencies(state),
  currencyConverter: storeState.currencyConverter,
});

const stateProps = returntypeof(mapStateToProps); 
type Props = typeof stateProps & typeof dispatchToProps;
type State = {};

class CurrencyConverterContainer extends React.Component<Props, State> {
...

المصدر: https://github.com/piotrwitek/react-redux-typescript-patterns#react -connected-component

يبدو أن حل kube وحزمتهم https://github.com/kube/returnof يعملان كما هو متوقع! 👍

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

تحرير: يبدو أنه تم دمج الأدوية الاختيارية الاختيارية الآن ، مما يعني أن إصدار لغة النوع التصريحي kube فقط ( type Return<T extends () => S, S> = S -> type helloReturnType = Return<typeof hello> ) سيصبح قابلاً للتطبيق. :د

تصحيح: كلا ، لا يبدو أن الدعم المدمج للقيم العامة الافتراضية يسمح بتحديد جزء من الأدوية الجنسية بينما يسمح للآخرين بالعودة إلى قيمهم المستنتجة ؛ ينتج عن Return<typeof hello> الخطأ Generic type 'Return' requires 2 type argument(s). .

@ tycho01 نعم أصبت بخيبة أمل لأنها لم تحل المشكلة.

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

14400

يمكنك عمومًا الحصول على نوع إرجاع دالة عبر مجموعة من دالة محيطة وهمية وكتابة الاستدلال:

المحيطة function returnTypeOf<RT>(fn:(...rest:any[])=>RT):RT {return void 0};
محليًا يحصل var r = returnTypeOf(someFunction); على قيمة undefined

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

type RT = typeof r;

سيكون الأمر أسهل كثيرًا إذا قمنا في البداية بتوسيع مفهوم typeof للسماح بـ returntypeof أو حتى استنتاج أفضل لهذا الاستخدام من typeof fn(a,b,c,...) ، والذي يمكنه بعد ذلك التقاط أنواع إرجاع توقيع مختلفة . يتم تنفيذ هذا الاستيعاب من نوع الإرجاع بالفعل بواسطة TS داخليًا.

سيكون typeofexpression () نوعًا من العودية من نوع الإرجاع مختلطًا بعمليات النوع: على سبيل المثال

type E = typeofexpression (f(1) + g("x"))

يكون

type E = typecomprehensionof (typeof f(1) + typeof g("x"))

التي يمكن فهمها مثل أحد

type E = typecomprehensionof (string + string) ie string
type E = typecomprehensionof (string + number) ie string
type E = typecomprehensionof (number + number) ie number

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

تعديل ------------------------------------------------- ------------

قصدت إضافة ... هذا مهم بشكل خاص لأي شخص يجب عليه استخدام Function.bind و Function.apply و Function.call لأن هذه العناصر تُرجع حاليًا من النوع any وهذا يعني أنه يجب أن يكونوا من النوع- باستمرار تم التعليق عليها لضمان عدم خروجها من عملية التحقق من النوع.

أن تكون قادرًا على الإشارة إلى نوع الإرجاع لوسيطة الوظيفة ستكون ... نعيمًا ...

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

أن تكون قادرًا على الإشارة إلى نوع الإرجاع لوسيطة الوظيفة ستكون ... نعيمًا ...

أنا أحب typeof <expression> الحالي الأفضل ، ومسألة نوع الإرجاع مغطاة بالفعل في # 12342.

كنت أستخدم فقط typeofexpression و typecomprehensionof كوسيلة للتمييز بين تلك الاستخدامات في الشرح. أفضل بناء الجملة الفعلي typeof (expression) .

كانت وجهة نظري أن فهم نوع التعبير يتطلب فهم نوع إرجاع دالة ... f(1) تعبير أيضًا. # 12342 مرتبط بهذه الطريقة. أنهم لا يستبعدون بعضهم البعض.

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

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

ماذا عن استخدام التعبيرات لاستدعاء الوظيفة typeof ، كما في الاقتراح ، ولكن باستخدام الأنواع مباشرة بدلاً من المعلمات؟

على سبيل المثال

function myFunction<T>(param1: T, param2: string, param3: number): T & {test: string} {
  // ...
}

type ResultType = typeof myFunction({hello: string}, string, number)
// ResultType is: {hello: string} & {test: string}

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

type ResultType = typeof myFunction(typeof obj, string, number)
// ResultType is: typeof obj & {test: string} 

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

كيف فعلت ذلك من أجل حالتي البسيطة:

interface IAlertMessage {
  addedAt: number;
  text: string;
  type: "error" | "warning" | "info" | "success";
}

declare let alertMessageInterface: IAlertMessage;

const messages: IAlertMessage[] = [];

function addMessage(text: string, type: typeof alertMessageInterface.type): void {
  messages.push({addedAt: new Date().getTime(), text, type});
}

addMessage("something", "info"); // <- OK - and also has intellisense for the second parameter (after typing first " of the second parameter, press Ctrl+Space in VSCode)
addMessage("something", "fatal"); // <- Compilation error : error TS2345: Argument of type '"fatal"' is not assignable to parameter of type '"error" | "warning" | "info" | "success"'.

ما سبق يعمل أيضًا مع أعضاء الواجهة نفسها:

declare let myIface: MyInterface;

interface MyInterface {
    prop1: {
        big: {
            complex: {
                anonymous: { type: {} }
            }
        }
    },
    prop2: typeof myIface.prop1.big.complex.anonymous;
}

ميزة استخدام declare let ... بدلاً من type ... بالنسبة لي هي أنه لا ينتج أي شيء في ملف الإخراج .js.

varadero هذا لا يحل المشكلة المطروحة ، وهي استرداد نوع الإرجاع لاستدعاء الوظيفة. كل ما تفعله هو استرداد نوع خاصية من النوع ، وهي ميزة مدعومة منذ TypeScript 2.2 ، على ما أعتقد.

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

سيكون من الرائع أن يكون لديك!

نظرًا لأن type KEYOF<T extends any[]> = keyof T[0] موجود بالفعل ، فإن typeof T[0]('something') سيعمل أيضًا مع هذا التنفيذ ، بافتراض function myFunc<T, K extends KEYOF<T>>(type: K)>{ } و myFunc([(r: string) => r]) (ستُرجع سلسلة لـ typeof T[0]('something') ) ؟

سيكون هذا قويًا مع this متعدد الأشكال.

حان الوقت لحدوث هذا!

إعادة قراءة هذا الموضوع ، حاولت فهم ما نفعله باستخدام typeof بناء الجملة ولماذا.

من الواضح ، في حالة typeof foo التافهة التي تم تنفيذها بالفعل ، فإنها تعطي نوع foo . (أتخيل أن pet is Fish محصن ضد استخدام بناء جملة خاص.)

في قراءتي الحالية لما يجب أن تفعله هذه الكلمة الرئيسية في الاقتراح الحالي ، فإن الكلمة الرئيسية نفسها لا تفعل أكثر مما تفعله الآن ، والاقتراح الحالي في الواقع لا علاقة له بالكلمة الأساسية typeof .

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

بالنظر إلى ذلك ، أفترض أنه بينما يحتاج $ typeof fn<A>(string) typeof لرفع متغير مستوى التعبير fn للاستخدام على مستوى النوع ، Fn<A>(string) من ناحية أخرى ، مع Fn كعنصر عام يحتوي على وظيفة ، لن يتطلب ذلك ، وبالتالي يمكن "تطبيقه" للحصول على نوع الإرجاع المناسب هنا دون الحاجة إلى typeof .

في هذا التفسير ، سوف نتحقق من استدعاءات الوظائف بعد أي من أنواع الوظائف المحتملة: بجانب typeof fn(...) / typeof fn<...>(...) أيضًا Fn(...) / Fn<...>(...) ، إن لم يكن حتى الوظيفة حرفي ((foo: Foo) => Bar)(Baz) / + الأدوية. خلاف ذلك ، يجب أن تظل خطة الهجوم كما هي.

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

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

تحرير: يمكن للنوع العام أن يُرجع بالفعل نوع دالة ، والذي يمكن أن يكون له أيضًا أنواع عامة. هذا يعني أن التبديل الآخر سيكون GetFn<A><B>() ، حيث تنتمي المجموعة العامة الأولى إلى استدعاء النوع العام ، تنتمي الأخيرة إلى استدعاء الوظيفة. لا يوجد GetFn<A><B><C>() على الرغم من أن GetFn<A><B>()<C>() سيكون شرعيًا أيضًا. ومع ذلك ، يظل الاستنتاج السابق بدون تغيير: يمكن أن يحتوي أي نوع على دالة وبالتالي (من المحتمل) تطبيقه كدالة.

تحرير 2: لقد أدركت للتو أنه سيكون هناك غموض مؤسف في X<Y>() . الآن ، X سيكون نوعًا يعيد نوع دالة.

  • إذا لم يكن X عامًا ، فهذا واضح - <Y> ينتمي إلى استدعاء الوظيفة.
  • إذا كان X عامًا ويتطلب المعلمة ، فهذا واضح أيضًا - فهو ينتمي إلى النوع ، ولا يتم تمرير أي معلمات للنوع للوظيفة.
  • إذا كان X يحتوي على عام اختياري ، يصبح هذا غامضًا.

    • في أحد التفسيرات ، يتم تمرير X معلمة نوع ، بينما لا يتم تمرير الوظيفة.

    • في تفسير آخر ، يُترك X لاستخدام معلمة النوع الافتراضي الخاصة به ، بينما يقوم <Y> بتعيين معلمات استدعاء الوظيفة بدلاً من ذلك.

لست متأكدًا بعد ما هو الحل الأفضل هنا.

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

icholy : نعم ، هذه التعديلات تضيف المزيد إلى وجهة نظرك ، أعلم.

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

wat

icholy : باختصار ، إذا كانت "الوظيفة" عامة / نوع ، فهل سنظل نكتب typeof ؟

إذا تم القيام بذلك بحيث يتفاعل مع الأحمال الزائدة بشكل صحيح ، فإنه يمنحك في الواقع وظائف "متغيرة" مجانًا (إنها فقط مجاملة بدلاً من وجود> 1 arity) ، حيث يمكنك تحديد نوع الإرجاع للدالة بشكل متكرر من حيث تطبيق أحد أحماله الزائدة ، مع بعض الحمل الزائد في الحالة الأساسية. هذه هي الطريقة التي تتم بها الأشياء في Haskell ، وستكون ميزة رائعة في TypeScript.

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

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

هل يمكنك ربما إظهار بعض الشفرات الزائفة حول كيفية رؤية هذه الفكرة تعمل للوظائف المتنوعة مثل Object.assign (تخطي التفاصيل مثل & مقابل Overwrite كما اعتدت في PoC الخاص بي مقابل R.mergeAll

@ tycho01 كنت ألعب برمز مثل هذا:

interface Pop<TVarStack extends VarStack> {
    (a: TVarStack["head"]): Pop<TVarStack["tail"]>
}

interface VarStack<THead = any, TTail extends (void | VarStack) = any> {
    head: THead
    tail: TTail
    <TNew>(a: TNew): VarStack<TNew, this>
    (): (a: Pop<this>) => any
}

// Figure out implementation later :)
let stack: VarStack<void, void>;
const pop = stack(new Date())(10)("Bob's")("your")("uncle")()

// a, b, c, d, and e are well-typed
pop(a => b => c => d => e => {
    // Need this because can't figure out how to encode base-case in Pop recursion
    return stack
})

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

إذا كان علينا الوصول إلى typeof ، فسأكون قادرًا على حل مشكلة الحالة الأساسية لـ Pop ، نظرًا لأن returnof(overloadedFunction(T)) سيعطيني بشكل أساسي طريقة للقيام بالنمط- مطابقة على مستوى النوع.

@ tycho01 لا يمكنني القيام بذلك على وجه التحديد مقابل Object.assign ، لأنه كما قلت ، هذا يعمل فقط للوظائف المنعشة ، لكنني سأحاول إنشاء وظيفة assign من أجلك والتي تعمل تقريبًا نفس الطريقة.

أتمنى أن يعمموا نوع الجبر قليلاً ، بحيث يكون على سبيل المثال (a: string) => (b: number) => void مجرد سكر مقابل Func<string, Func<number, void>> ، و (a: string, b: number) => void كان مجرد سكر مقابل Arity<number, Arity<string, void>> ، أو شئ مثل هذا. ثم نحتاج فقط إلى أدوات للأغراض العامة لتحويل الأنواع من أجل الحصول على الكثير من الميزات المثيرة للاهتمام بشكل ناشئ.

نعتذر عن المشاركة الثلاثية. @ tycho01 هنا كاري ، "variadic" assign :

interface Assign<TCurr extends {} = {}> {
    <TAdd extends {}>(a: TAdd): Assign<TCurr & TAdd>
    (): TCurr
}

let assign: Assign = undefined // implement somehow
const result = assign({ foo: "bar" })({ baz: 42 })()

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

الفكرة تعجبني؛ بالتأكيد لم أفكر في اتجاه الواجهات بنفس القدر.
إذا تمكنا من تغيير JS للكتابة ، فربما سأطلب من TC39 تغيير Object.assign إلى mergeAll غير متغير. :د

ليس مثل التكرار القائم على الزيادة مثل هذا الذي عمل بالفعل مع الوظائف في الممارسة حتى الآن على الرغم من (# 17086) ... ولكن في كلتا الحالتين أنا معك ، سيكون لهذا الاقتراح تأثير أكبر من أي شيء آخر رأيته.

@ tycho01 أعتقد أنه من غير المحتمل أن تحصل على قدر كبير من الجر إذا طلبت تحويل الوظائف المتنوعة إلى وظائف arity ثابتة ، نظرًا لوجود تكلفة وقت تشغيل للوظائف المنبثقة أو التطبيق المتكرر الذي تستخدمه JS (أو على الأقل محركات JS الموجودة وكودها) ربما لم يتم تحسينه بشكل كبير لـ. أعتقد أن ما نحتاجه بدلاً من ذلك هو أن تجعل TypeScript من السهل أيضًا بناء وتفكيك أنواع للوظائف arity> 1 بشكل متكرر. أعتقد أن كل هذا يعود إلى # 5453.

قد يجد الآخرون أنه من المثير للاهتمام معرفة أن c ++ لها ميزة مشابهة جدًا: نوع التراجع (تعبير)

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

سأكون سعيدًا حقًا إذا تمكنا من تنفيذ حتى مجموعة فرعية من هذا الاقتراح. وهي واحدة:
type A = typeof anotherVariable . يمكنني أن أفعل كل شيء بهذا السطر فقط. interface B extends A ، <B & A> : B & A ، إلخ.

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

ما انتهى بي الأمر هو إنشاء فصلين. يمتد أحد الفصول إلى React.Component ويضيف جميع الطرق الإضافية ، بينما تتعامل الفئة الموجودة داخل وظيفة HOC مع render() .

يعملblindbox type A = typeof anotherVariable بالفعل:

var data = [1, 2, 3].map(v => ({ raw: v, square: v * v }));

function checkItem(item: typeof data) {
    // item is Array<{ raw: number; square: number; }>
}

Igorbek واو ، أنت على حق.

حسنًا ، اكتشفت سبب عدم نجاحها من جانبي.
هذا لن ينجح

interface B {
  thisIsB: boolean
}

const typeTest = (type: any) => {
  return 1 as any as App & B
}
type A = typeof typeTest(1)
declare const aVal: A; // aVal's type isn't of type App & B, but something else.

هذا مع ذلك ، سوف يعمل.

interface B {
  thisIsB: boolean
}

const typeTest = (type: any) => {
  return 1 as any as App & B
}
const typeTestVal = typeTest(1)
type A = typeof typeTestVal
declare const aVal: A; // aVal's type is of type App & B!

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

function someFunctionWithComplexReturnTypeThatUsesGenerics<T>(item: T) { ... }

// it is impossible to work this around because there's no chance to create a fake variable that would be parameterized by T
function use<T>(item: typeof someFunctionWithComplexReturnTypeThatUsesGenerics<T>(item)) { ... }

@ Igorbek :

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

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

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

سأكون مهتمًا برؤية المزيد من أفكارك حول النهج المقترح في الأصل. من المسلم به أن هذا قد يعمل على كشف المزيد قليلاً عن مستوى النوع: عوامل التشغيل المستندة إلى JS (فكر في ! && || + - * / instanceof ) بالإضافة إلى عوامل تشغيل محددة لـ TypeScript (عامل التأكيد ! ).
الشيء الذي يدور حول مشغلي JS هو ... أنهم غير مجديين على مستوى النوع كما هو ، حيث إن السماح لهم بالعمل على القيم الحرفية لإنتاج أنواع النتائج الحرفية المقابلة يعتبر حاليًا خارج النطاق ( المرجع ) - التعبير -المستوى 1 + 1 فقط ينتج النوع number ، وما شابه ذلك بالنسبة للآخرين.
مع وضع هذا في الاعتبار ، فقد كنت معنيًا بنفسي في اقتراحهم القائم على النوع.

هل ستنتقل هذه الميزة إلى TypeScript؟ [...] نحن بحاجة للحفاظ على هذا الموضوع.

لقد اقترحت هذا الاقتراح كحل أكثر عمومية لأعراض أصغر هنا ، على الرغم من نجاحه المحدود.

@ tycho01 إن الحجج الخاصة بي للتباين القائم على التعبير typeof هي نفسها تقريبًا كما هو مذكور في الأصل بواسطة yortus :

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

    • لا يحتاج المستخدمون إلى تعلم بناء جملة جديد

  • البساطة : يبدو أن TypeScript يحتوي بالفعل على جميع التسهيلات المطلوبة لتنفيذ هذه الميزة بطريقة غير مزعجة (تم إثبات ذلك من قبل العلاقات العامة)
  • الاكتمال : تكون التعبيرات على الأقل لها نفس التعبيرات مثل الأنواع ، حيث يمكننا دائمًا استخدام تأكيدات النوع ( (undefined as any as <any arbitrary type can be here>) ) ، لكن في بعض الأحيان يفتقر نظام الكتابة إلى الأنواع التي يمكن التعبير عنها في التعبيرات (تم تقديم أنواع الانتشار والباقي بعد تم هبوط التعبيرات المقابلة).

شكرًا لك @ tycho01 على أنك أحضرت هذه النقطة إلى promised PR (لقد أتيت إلى هنا بالفعل بعد تعليقاتك هناك) ، وهذا يوضح حقًا كيف يمكن لميزة بسيطة وعامة أن تغطي سيناريوهات أكثر تعقيدًا بطريقة أنيقة للغاية دون الخبز في سلوك محدد للغاية في اللغة.

أرى typeof الممتد كمغير حقيقي للعبة لتعبير نظام النوع ، مثل الأنواع المعينة / keyof فعلت ذلك.

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

أعتقد أنك تُظهر قيمة اليوم للاقتراح الأصلي بشكل أفضل من المنشور الأصلي اليوم ، لأن TS الحالي (أو حسنًا ، Playground 2.3.3 ) يمكن أن يجعل العديد من السيناريوهات الأصلية تعمل بالفعل:

// I have a strongly-typed collection but the element type is anonymous/unknown, how can I reference the element type? (#3749)

// Mapping to a complex anonymous type. How to reference the element type?
var data = [1, 2, 3].map(v => ({ raw: v, square: v * v }));
declare function checkItem(item: typeof data[0]): any
// ^ no longer errors, needs no further change

// A statically-typed dictionary. How to reference a property type?
var things = { 'thing-1': 'baz', 'thing-2': 42 };
type Thing2Type = typeof things['thing-2'];
// ^ no longer errors, needs no further change

// A strongly-typed collection with special indexer syntax. How to reference the element type?
var nodes = document.getElementsByTagName('li');
type ItemType = typeof nodes.item(0);
// ^ the `.item` access works, but function applications still errors, would be fixed by either version of the proposal.

// A function returns a local/anonymous/inaccessible type, how can I reference this return type? (#4233, #6179, #6239)

// A factory function that returns an instance of a local class
function myAPIFactory($http: HttpSvc, id: number) {
  class MyAPI {
    constructor(http) {}
    foo() {}
    bar() {}
    static id = id;
  }
  return new MyAPI($http);
}
declare function augmentAPI(api: typeof myAPIFactory(HttpSvc, number) /* ERROR */): any
// ^ function applications errors, would be fixed by either version of the proposal, if using slightly different syntax.

// I have an interface with a complex anonymous shape, how can I refer to the types of its properties and sub- properties ? (#4555, #4640)

// Declare an interface DRY-ly and without introducing extra type names
type MyInterface = {
  prop1: {
    big: {
      complex: {
        anonymous: { type: {} }
      }
    }
  },
  // prop2 shares some structure with prop1
  prop2: MyInterface['prop1']['big']['complex'];
}
// ^ no longer errors after swapping `.k` access to `['k']`. `typeof` was unnecessary and counter-productive -- MyInterface isn't exposed on the expression level.

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

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

وبالمثل ، فإن أسلوب التعبير أولاً سيسمح أيضًا بحقن الأنواع باستخدام <MyType> whatever أو whatever as MyType ، على الرغم من أنه مثل typeof في أسلوب النوع ، فإنه سيبدو بالمثل وكأنه فكرة لاحقة: الأسئلة حول تطبيق الوظائف المخزنة في الأنواع / الأدوية ، والتي من المحتمل أن تشكل معظم القيمة المضافة الفعلية لهذا النهج القائم على النوع (على الرغم من عدم ذكره هناك) - الاستدلال الدقيق للوظائف ذات الترتيب الأعلى ، وكذلك القول الشرطي القائم على النوع كما هو الحال في هذا الموضوع promised . * والأسوأ من ذلك ، على عكس حالة استخدام OP ، فهذه ليست لها حلول.

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

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

*: لقد أدركت للتو أنك من المحتمل أن تقوم بالوظائف ذات الترتيب الأعلى من خلال الرجوع إلى الوظائف في المعلمات من خلال أسماء المعلمات الخاصة بها ، بدلاً من التقاط أنواعها في العوامل العامة.
يبدو أنه يمكن في الواقع تحويل الأشياء في كلا الاتجاهين: يساعد typeof في الرفع من مستوى القيمة إلى مستوى النوع ، بينما يساعد declare let y: x; في رفع الأشياء من مستوى القيمة إلى مستوى النوع. غير أنيق مثل حل OP ، لكن نعم.
إذا تم إحضار أنواع الوظائف من خلال ، على سبيل المثال ، الأدوية الجنسية الصريحة ، أعتقد أن هذا لن يساعد - سيكونون على مستوى النوع دون وسيلة لنقلهم.
إذا كان هذا يبدو وكأنني آمل بشكل استباقي في تغطية القواعد المتعلقة بالوظائف ، فمن المحتمل أن يكون ذلك بسبب حظر الكثير من تقدمي في تحديات الكتابة التي لم يتم حلها في هذه الوظيفة الإضافية (مع الإشارة البارزة إلى 5453). ولكن إذا تمكنا من اكتشاف هذه الأشياء ، فسيكون الأمر يستحق ذلك.

تحرير: لقد فكرت في عدد قليل من الحالات النظرية التي قد يتم استبعادها في نهج قائم على التعبير الآن ، على الرغم من أنه لم يخطر ببالي أي أحداث عملية حتى الآن:

  • تم تمرير الوظائف من خلال عام مقدم صراحة.
  • تم إرجاع الدوال بواسطة وظائف المعلمات ، ليتم التقاطها بشكل عام ، على سبيل المثال (باستخدام بناء جملة الاقتراح القائم على التعبير) declare function f<G extends (...args: any[]) => R, R extends <T>(foo: T) => Bar<T>>(g: G): typeof R(baz); // error following the expression-based proposal: R is not exposed at the expression level . يمكن بالطبع حساب هذه إذا كنت تعرف ويمكن أن توفر أنواع المعلمات G ، ولكن لا توجد طريقة للحصول على هذه المعلومات afaik حتى الآن (ما لم يكن ربما # 14400؟). أفترض أن هذه الفئة ستشمل الوظائف التي تعمل على وظائف المصنع المستخدمة في AngularJS المذكورة أعلاه.

Igorbek لا أفهم ما تتوقعه من هذا المقتطف:

function use<T>(item: typeof someFunctionWithComplexReturnTypeThatUsesGenerics<T>(item)) { ... }

يبدو وكأنه تعريف دائري.

masaeedu آسف ، قصدته:

(التبديل إلى f بدلاً من someFunctionWithComplexReturnTypeThatUsesGenerics )

function use<T>(item: typeof f<T>(undefined as any as T)) { ... }

فقط للتوضيح ، هذا هو ما لا يمكن حله حاليًا عن طريق إدخال متغير مزيف:

const _typeofItem = f<T>(undefined as any as T); // no T here
function use<T>(item: typeof _typeofItem) { ... }

راجع للشغل ، أدركت للتو أن الاقتراح الحالي يتعارض مع عامل نوع الاستعلام الخاص بنوع العضو T[K] لأن typeof له أولوية أعلى:

  • typeof x[K] يعني حاليًا (typeof x)[K] حيث K _ هو نوع
  • إذا تم أخذ الاختلاف القائم على التعبير ، فسيتم إدخال غموض:

    • typeof x[k] يعني (typeof x)[k] حيث k هو نوع ؛ أو

    • typeof x[k] يعني typeof (x[k]) حيث k هو تعبير

أفضل في الواقع typeof (x[k]) لأنهما متكافئان من الناحية اللغوية ، لكنهما تغيير جذري بالتأكيد.

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

image

Igorbek : نعم ، يبدو أن هذا هو السبب في أن type Thing2Type = typeof things['thing-2']; من المنشور الأصلي يعمل بالفعل.

dehli : أنت تقارن مستوى تعبير JS بمستوى نوع TS - فهما ليسا متماثلين. أحدهما يعمل في المتصفحات / العقدة ، والآخر يعمل في مترجم TS.

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

@ tycho01 مسكتك . افترضت أن TS سيستخدم نفس ترتيب الأسبقية مثل JS.

dehli ، مستويات التعبير هي نفسها ، نعم. على مستوى الكتابة ، هناك شيء آخر له نفس البنية ، لكنه يعيد نوعًا بدلاً من سلسلة.

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

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

Igorbek هل هذه حالة الاستخدام الأساسية المتبقية لـ typeof على التعبيرات العشوائية؟ إذا حصلنا على 14400 # ، فسيمنحنا ذلك returnof باستخدام أنواع محددة من قبل المستخدم إذا لم أكن مخطئًا.

14400 لن يمنحنا returnof بصيغته العامة:

type Return<T extends () => R, R = any> = R;

// overloaded function
declare function f(item: number): number;
declare function f(item: string): boolean;

type fType1 = Return<typeof f>; // ??
type fType2 = typeof f(1); // number
type fType3 = typeof f('a'); // boolean

// generic function
declare function g<T>(item: T): T;

type gType1 = Return<typeof g>; // ??
type gType2 = Return<typeof g<number>>; // not supported syntax
type gType3 = typeof g(1); // number
type gType4 = typeof g<number>(1); // number
type gType5 = typeof g('a' as 'a'); // 'a'

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

  • _ الأنواع المعيّنة الشرطية_ التي هي أقوى بكثير مما يمكننا الحصول عليه مع # 12424 ، لأن typeof سيستخدم دقة التحميل الزائد القياسية ؛ أظهر @ tycho01 مثالًا أنيقًا في أنواع promised PR # 17077
  • الحصول على أنواع الوظيفة / الطريقة التي ترجع في سياق الأنواع العامة (كما أوضحت من قبل)

masaeedu : سؤال جيد. النسخة القصيرة هي إلى حد كبير ما قاله Igorbek ؛ لمزيد من التفاصيل حول التحديات الملموسة التي تم حلها ، ستجد قائمة مختصرة في قائمة "أهم الميزات المطلوبة" في # 16392 ، حيث حاولت ربط التحديات التي لم يتم حلها بالمقترحات التي يمكن أن تتيحها.

هذا يشمل:

  • وظائف المصنع مثل المستخدمة من قبل AngularJS على سبيل المثال ، انظر الخيوط الثلاثة المرتبطة في المنشور الأصلي هنا. - من الناحية الواقعية ، يجب أن يكون هذا جيدًا بالنظر إلى ReturnType المدمج حديثًا.
  • بالنظر إلى المدخلات المعروفة ، يمكنك فجأة حساب أنواع الإرجاع الدقيقة لأشياء مثل reduce / map / filter / find - أي شيء يتضمن التكرار والشيكات. سيستفيد هناك stdlib.d.ts وكذلك أعضاء FP مثل Ramda / Lodash. من الناحية العملية ، قد لا تكون جميع المدخلات معروفة (مصفوفات تشبه القوائم أكثر من المصفوفات التي تشبه القائمة) ، ولكن يمكن كتابة عمليات map و filter على الكائنات بشكل أفضل من كتابة Partial فقط. يعد هذا ضروريًا لكتابة مكتبات إدارة الحالة بشكل أفضل مثل redux (راجع https://github.com/piotrwitek/react-redux-typescript/issues/1) و Angular's ngrx ، والتي حتى ذلك الحين تم إجبارها على إبقاء واجهة المستخدم الخاصة بها منخفضة المستوى لأنه بدون عملية كتابة جيدة map لا توجد طريقة لهم لحساب النتيجة المرجوة على مستوى النوع من بيانات الإدخال DRY'er.
  • حاليًا ، لا يمكن لطباعة تكوين الوظيفة أن تأخذ أنواع الإرجاع المعتمدة على الإدخال في الاعتبار أيضًا.
  • كما سيجعل من الممكن فجأة البدء في كتابة أشياء مثل العدسات. - أفترض تقنيًا أن هذا لا يزال محظورًا هنا فقط في عمليات التحرير بأنواع الإرجاع المعتمدة على الإدخال. وهو حقا رفاهية. تغيير الصفوف لا يزال بحاجة (جزء من) # 5453 بالرغم من ذلك.
    كما أنه يتيح:
  • العمل على القيم المنطقية على مستوى النوع ، والذي كان مستحيلًا حتى الآن
  • أنواع فك التغليف لعمليات شبيهة بالعمليات مثل $ # flatMap promised
  • إضافة قيود على إدخال النوع / الوظيفة ، وموضوع # 15347 وفكرة حصلت عليها من كتاب "تطوير يحركها النوع مع إدريس" حول الأنواع التابعة. قد يساعد هذا في قول عدم السماح بالقواسم على 0 - وهي عبارة عن اختراق للطرح من الأنواع ، موضوع # 4183. أنا متأكد من أنني لم أتخيل معظم حالات الاستخدام هناك حتى الآن ، ولكن يمكن فعل ذلك مع هذا ، على سبيل المثال function div<B extends number, NotZero = { (v: '1') => 'whatever'; }({ (v: 0) => '0'; (v: number) => '1'; }(B))>(a: number, b: B) . عادةً ما تعلن لغة إدريس من خلال عمليات متجه / مصفوفة آمنة الطول ، ولكن هذا يحتاج فقط للوظائف ذات الترتيب الأعلى.
  • طريقة للتعبير عن قيود Union<T> / NonUnion<T> على أنواع المدخلات باستخدام القيود المذكورة أعلاه + IsUnion . - هذا النوع كسر نوعًا ولست متأكدًا من كيفية القيام بذلك بعد الآن.
  • بالنظر إلى # 5453 أيضًا ، يساعد هذا أيضًا في كتابة وظائف مثل curry ، Function.prototype.bind ، Promise.all (ليست قائمة شاملة ، فقط بعض الوظائف التي أثارها الأشخاص الآخرون على أنها صعبة).
  • المنطق switch المقترح في # 13500 ، تم حله هنا من خلال مطابقة نمط التحميل الزائد للوظيفة
  • تلك الأنواع الشرطية المعينة التي هي موضوع # 12424 و # 17077 ( promised ) ، ويعرف أيضًا باسم عمليات التحقق من النوع على مستوى النوع. ومع ذلك ، فإن هذا لن يحل المشكلة - حتى لو كان ذلك قابلاً للاستخدام لإنتاج حرفية منطقية في أي مكان اليوم ، حتى هذه الأراضي # 6606 ، ما زلنا لا نملك أي طريقة للعمل على / القيام بالشروط بناءً على هذه المعطيات المنطقية على مستوى النوع حتى الآن على أي حال.
  • التحقق من وجود فهرس السلسلة على أنواع الكائنات مثل الحفاظ على هذا عبر العمليات (مثل Omit ، Overwrite من # 12215) ، انظر تعليقي في # 12424
  • استنادًا إلى عمليات التحقق من فهرس السلسلة أعلاه ، فإن طريقة إصلاح الوصول إلى خاصية نوع الكائن ، مثل التحقق من السلاسل المطابقة لأسماء أساليب النموذج الأولي للكائن (على سبيل المثال ، toString ، toLocaleString ) ستحل إلى فهرس السلسلة بدلاً من ذلك من أساليب النموذج الأولي ، مما قد يؤدي إلى إصلاح مواطن الخلل المتعلقة بالوصول إلى الكائن toString عبر جميع عمليات النوع الأخرى التي كانت تعتمد في السابق على الوصول إلى خاصية الكائن المضمنة المعيبة.

تحرير: حالات الاستخدام المشطوب التي تم ملؤها بالأنواع الشرطية (# 21496) منذ وقت كتابة هذا التقرير.

اقتراح Igorbek لنهج التعبير أولاً (على حساب ربما كسر بعض التطبيقات غير المعروفة لجزء من القائمة أعلاه - لم أفكر حتى الآن في أي شيء ملموس على الرغم من أنني أخشى أن أتعرض للوقوع في الزاوية ) وعد أيضًا بسد الفجوة بين مستويات القيمة والنوع ، والتي تتضمن حاليًا أشياء مثل تطبيق الوظيفة هذا (هذا # 6606) ، الانتشار / الراحة (# 5453) ، عامل التأكيد ! (# 17370) ، نوع الاتحاد الطرح من خلال واقيات الكتابة (# 4183 ، يمكن تحقيقه بخلاف ذلك من خلال القيود المذكورة أعلاه) ، وربما أكثر من ذلك لا يمكنني التفكير فيه من أعلى رأسي.

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

تحرير: تحديث تعليق المقارنة الخاص بي أعلاه مع تحد إضافي محتمل.

تحرير 2:

من المؤسف هنا أن المنشور الأصلي سيجعلك تعتقد أن هذا سيسمح فقط بكتابة بعض الحالات المتطورة بدون حل بديل null & ، بينما في الواقع هذه هي الميزة الهامة التي تمنع TS في الوقت الحالي مع عدم وجود حلول معروفة للسياقات المحيطة .

هذا يعني أنه يؤثر على TS المكتوبة في ملفات .d.ts منفصلة ، وهذا هو المعيار ليس فقط لـ stdlib.d.ts ، ولكن أيضًا لجميع كتابات DT المكتوبة بشكل منفصل عن مشاريع JS الأصلية ، والتي نادرًا ما تتغير بينما يريد JS libs البقاء مفتوحًا لبدائل مثل Flow أيضًا.

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

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

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

  • نوع الإرجاع لنوع دالة ، عند تطبيقه مع وسيطات من أنواع معينة
  • (قبل typeof K[L] ) نوع الخاصية في نوع الكائن الذي يتوافق مع مفتاح من النوع الحرفي للسلسلة
  • (ربما آخرون ، أحتاج إلى مراجعة قائمتك عن كثب)

أشعر أنه ينبغي في الواقع أن يكون من الممكن إنشاء تعبيرات على مستوى الكتابة البحتة لهؤلاء ، دون اللجوء إلى المزج بين التركيبات على مستوى القيمة والتعبيرات على مستوى النوع. سيكون أمرًا رائعًا إذا كانت الأنواع مثل (a: A) => B أو A | B عبارة عن سكر على أنواع بسيطة ذات معلمات مثل Func<A, B> ، Union<A, B> ، وكان لدينا أدوات عامة لمعالجة الأنواع ذات المعلمات (HKTs أو Fundeps أو نوع العائلات ، وما إلى ذلك).

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

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

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

أعتقد أنه من المتوقع في الغالب تضييق الفجوة بين مستويات القيمة والنوع (هل يتحرك TC39 أسرع من TS؟) ، و afaik تحتوي الفجوات بالفعل على اقتراح بارز على مستوى النوع (انظر الجزء السفلي من رسالتي السابقة).

هيك ، أدركت أن إنشاء كتابات لبعض الوظائف سيكون خارج تجربة معظم المستخدمين.
الطريقة التي أنظر بها إلى هذا هي ، أريد أن تتم كتابة المكتبة القياسية و FP libs بشكل جيد بحيث يمكن لمستخدمي TS كتابة هؤلاء والاعتناء بهم تلقائيًا.
لا يوجد لدى TS سوى عدد قليل من المشغلين على مستوى النوع ، ولكن حل المشكلات الفعلية معهم (المثال الرئيسي هو # 12215 Overwrite / Omit ) قد يبدو أقل من علم الصواريخ لمطوري الويب العاديين. هيك ، لقد أخذنا حتى وقت قريب أيضًا ، وهم ليسوا حتى النموذج الأولي / الفهرس / الرمز - حتى الآن.

سيكون من الرائع استخدام أنواع مثل (أ: أ) => ب أو أ | كان B عبارة عن سكر على أنواع بسيطة ذات معلمات مثل Func و Union

يمكننا قلبها وإنشاء الأنواع ذات المعلمات كأسماء مستعارة / منشآت النوع. بالنسبة لعملية نوع تتطلب Foo<Bar> ، لا يهم ما إذا كان الشيء الخاص بك يتم تبسيطه لواحد - إنه يتحقق فقط مما إذا كان يناسب الوصف أم لا.
هذا إلى حد كبير ما يفعله stdlib.d.ts - لديك foo[] ، لكنه يفي بالوصف Array<Foo> وبالتالي فهو يعمل مع كتاباته Array.prototype .
ليس مثل هذا يساعد في الواقع على الرغم من:

type Union2<A, B> = A | B;
type TuplizeA<Tpl extends Union2<any, any>, A, B> = [Tpl[0], Tpl[1]];
// ^ sorry, no way to access union members through e.g. [0]
// type a = TuplizeA<1 | 2>;
type TuplizeB<Tpl extends any | any> = [Tpl[0], Tpl[1]];
// ^ sorry, no way to access union members through e.g. [0]
// type b = TuplizeB<1 | 2>;
type TuplizeC<Tpl extends Union2<A, B>, A, B> = [A, B];
type c = TuplizeC<1 | 2>;
// ^ need 3 arguments, maybe fixable with #14400
type TuplizeD<Tpl extends A | B, A, B> = [A, B];
// ^ need 3 arguments, maybe fixable with #14400
type d = TuplizeD<1 | 2>;

حسنًا ، لم تحل المشكلة ولكن أدركت أن kube's # 14400 يمكن أن يساعد بالفعل هناك. وقد رأيتك هناك في وقت سابق اليوم أيضًا!
نفس الشيء بالنسبة للوظائف في الواقع - وهو تذكير جيد بأن # 14400 قد لا يقوم فقط بعمل أنواع إرجاع هناك ولكن أنواع المعلمات أيضًا.

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

تحرير: لتجاوز تلك الفجوات الوظيفية على مستوى التعبير / النوع مرة أخرى:

  • تطبيق الوظيفة: هذا # 6606
  • عامل التأكيد ! (# 17370): بعد هذا # 6606 تم حل هذا
  • طرح نوع الاتحاد من خلال حراس النوع (# 4183): مجرد حالة عامة من ! أعلاه ، مع هذا # 6606 يمكن تحقيقه أيضًا من خلال القيود (على سبيل المثال NotZero أعلاه).
  • الانتشار / الراحة (# 5453) - حتى إذا كان لا يمكن معالجة المجموعات حتى تصل إلى هذه الأرض ، يمكن لـ ArrayLike s المتشابهة أن ترى عمليات List في مضموني . مع هذا # 6606 أعتقد الآن أنه يمكننا استخراج المعلمات من الوظائف غير المطبقة ، على الرغم من استخراجها في لحظة التطبيق (أي للحصول على قيم الإدخال الدقيقة) ، سيظل بحاجة إلى 5453.

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

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

تحرير 2:

لقد أدركت للتو أن خدعة اقتراح التعبير لجلب الأنواع إلى مستوى التعبير ، 1 as any as MyType ، يجب أن تعمل منطقيًا مع الوظيفة نفسها أيضًا.

ربما يعني هذا أن الوظيفة الفعلية التي تم تمكينها بواسطة كلتا النكهات تبدو متشابهة إلى حد ما ، وتتكون الاختلافات الخارجية في الغالب من typeof myVar (نكهة النوع) مقابل myVar (نكهة التعبير) لاستخدام المتغيرات في تطبيق الوظيفة ؛ لاستخدام الأنواع فيها MyType (نوع النكهة) مقابل 1 as any as MyType (نكهة التعبير ، البديل declare let a: any; ثم <MyType>a ).

يبدو أن تغييرات AST لأي منهما يمكن التحكم فيها أيضًا. تحتاج نكهة التعبير فقط إلى اقتران typeof للإشارة إلى تعبير قيمة بدلاً من ذلك ؛ سوف تنسخ نكهة الكتابة بناء جملة تطبيق الوظيفة الحالي ( fn<T>(arg) ) من مستوى التعبير إلى مستوى النوع ، والتثبيت في التنفيذ الحالي كما اقترحه Ryan أعلاه.

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

حالة نكهة التعبير:

  • typeof expr باستخدام بنية JS بدون حل بديل قبل دعم مستوى نوع TS

حالة نكهة النوع:

  • لا تغيير على الأولوية
  • لا تزال تعبيرات القيمة قابلة للإمساك بها من خلال الحل البديل (أو من خلال TS إذا كانت ضمن بناء جملة مختلف حتى يلحق المشغلون بالركب)
  • MyType بدلاً من 1 as any as MyType : لا يوجد مستوى نوع في مستوى التعبير في مستوى النوع الخاص بك في مستوى التعبير الخاص بك.

أحد الموضوعات ذات الصلة التي لم يتم لمسها حتى الآن هو كيفية توفير روابط this في هذا النوع من الوظائف "التطبيق". الآن ، قام مندوبو JS بالكتابة فوق هذا إلى طرق Function.prototype ، ولكن كما هو الحال ، فإن هؤلاء يفتقرون إلى طريقة للتعامل مع هذا على مستوى النوع.

صيغة المثال العشوائية ، مع تحديد نوع الوظيفة F (this: Foo, a: number, b: string) => SomeReturnType التي ربما تم استدعاؤها بطريقة أخرى كـ F(MyA, MyB) : F(this: MyFoo, MyA, MyB) .

سيظل حساب نوع الإرجاع دون استبدال الارتباط this مثل F(MyA, MyB) ، مما يعكس كيفية تجاهل وسيطة type-level this عادةً إذا حاولت استخدام مثل هذه الوظيفة في مستوى التعبير.

إيجابيات هذا المثال في بناء الجملة:

  • يعكس بناء جملة الإعلان

سلبيات هذا المثال في بناء الجملة:

  • يعكس بناء جملة الإعلان

إذن ، اتضح أن هذا موجود بالفعل في اللغة!

لا تتحمس كثيرا.

أشار DanielRosenwasser للتو إلى # 12146 ، وهو خطأ يسمح باستخدام استدعاءات الوظائف على العناصر الحرفية بدلاً من الأنواع.

_بعد 5 دقائق_

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

interface String {
    passthrough<T>(v: T): T;
}

// All work
type ANumber = "".passthrough(10 * 10);
type AString = "".passthrough("hello" + "world");
type AHelloWorld = "".passthrough("hello world");
type AnArbitraryThing = "".passthrough(Object.assign({hello: "world"}, {foo: "bar"}));
type ThisCraziness = "".passthrough((() => "cows are big dogs"));

~ هذا يجعل Effort: Difficult في هذه المسألة يبدو مشكوكًا فيه بعض الشيء ، يبدو أنهم فعلوا ذلك بالصدفة هناك.

استمتع @ tycho01.

TheOtherSamP لقد جربت هذا باستخدام TypeScript 2.4.2 وجميع هذه الأنواع يُستدل عليها على أنها any .

pelotom Huh ، إنها تعمل هنا على 2.4.2 و 2.5.0-dev.20170803. الهدف es6 ووضع صارم.

image

يبدو أنهم قاموا بإصلاحه للتو ، وأخشى أن يكون ذلك خطأي. # 17628

تضمين التغريدة اوه حسنا.

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

TheOtherSamP : هاها ، هذا مضحك جدا.
من ناحية الوقت ، يبدو أنهم بدأوا الإصلاح هناك قليلاً قبل تعليقك. اوه حسنا.

Pelotom :

لقد جربت هذا باستخدام TypeScript 2.4.2 وجميع هذه الأنواع يُستنتج أنها موجودة.

يبدو أن مقتطفه يعمل في Playground (2.3.2). في إصدار حديث خارجه ( ^2.5.0-dev.20170626 ) أواجه مشكلة في إعادة الإنتاج أيضًا.

هذا يجعل Effort: Difficult في هذه المسألة يبدو مشكوكًا فيه بعض الشيء ، يبدو أنهم فعلوا ذلك عن طريق الصدفة هناك.

كانوا يشيرون إلى تطبيق قائم على النوع ، مما يعني بعض التغييرات ، بينما يبدو أن هذا يستخدم لغة تعبير (-> + ، Object.assign ، استدعاءات وظيفية أخرى).

@ tycho01 _ أعتقد أن كل شيء بدأ من خلال لفت انتباهي إليه في # 17618. حسنًا ، هذا سيعلمني أن أفكر بجدية في استخدامه في الإنتاج.

كانوا يشيرون إلى تطبيق قائم على النوع ، مما يعني بعض التغييرات ، بينما يبدو أن هذا يستخدم لغة تعبير (-> + ، تعيين كائن ، استدعاءات وظيفية أخرى).

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

تم فتح العلاقات العامة على 17961 #.

yortus هل تغطي هذه الحالة "typeof literal"؟
لا يسمح TypeScript اليوم بكتابة "const x: typeof 1 = 1؛"

NN --- يغطي الاقتراح الأصلي جميع التعبيرات ، ولكن كما أفهمها ، فإن الجزء "المعتمد" يغطي فقط الوصول إلى الممتلكات وأنواع إرجاع الوظائف.

حتى لو تم السماح بـ typeof 1 ، لست متأكدًا مما إذا كان سيعطي النوع الحرفي ( 1 ) أم النوع الأوسع ( number ).

لا يسمح TypeScript اليوم بكتابة "const x: typeof 1 = 1؛"

لماذا لا const x: 1 = 1; ؟

SaschaNaz أردت أن أكتب شيئًا مثل

const a = {q:1};
const b = {q:1};
const x: ReadonlyArray<typeof a> = [a,b];

لكن المماثل لا يعمل مع القيم الحرفية:

const x: ReadonlyArray<typeof 1> = [1,2,3];

yortus نقطة جيدة حول النوع الدقيق. لم أفكر في الأنواع الحرفية ..

NN ---: أعتقد أن مثالك يعمل بالفعل.

@ tycho01 Flow لديك الآن $Call النوع للحصول على نوع الإرجاع للدالة https://github.com/facebook/flow/commit/ac7d9ac68acc555973d495f0a3f1f97758eeedb4

ألن يكون السماح لـ typeof fn(...) فقط هو نفسه السماح بتعبير تعسفي لـ typeof ؟

function fn() {
  return /** whatever expression you like */;
}
type a = typeof fn();

إلا أنك تقوم الآن بإنشاء وظيفة وقت التشغيل ليس لغرض آخر سوى اكتشاف نوع؟

ليس صحيحا. أنت تقوم بتقييم نوع لتعبير ، وليس تنفيذًا للتعبير.

@ dyst5422 typeof fn() لن يقيم التعبير فعليًا ، بل سيعطيك فقط نوع الإرجاع.

تحرير: ربما هذا ما كنت تحاول قوله ، لست متأكدًا. لكني أعتقد أن @ sky87 كان يتحدث عن _تعريف_ دالة بدون غرض ما عدا استخدامها في تعبير بنوع ، وليس _تقييمها ".

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

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

const dummy = (false as true) && /* arbitrary exp */;
type TypeOfExp = typeof dummy;

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

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

type TypeOfExp = typeof (
  false &
  "false" &
  0
)

لتكون قادرًا على إجراء تقييم نوع التعبير بشكل تعسفي

هل سيكون من الممكن الاستعلام عن نوع الإرجاع لاستدعاء new ؟ حالة الاستخدام الخاصة بي: أريد كتابة التعليقات التوضيحية من النوع لوظيفة تقبل الإشارة إلى أي تطبيق PromiseConstructorLike (على سبيل المثال $ q أو Bluebird) وإرجاع وعد تم إنشاؤه بواسطة هذا التنفيذ.

declare function wait<P extends PromiseConstructorLike>(time: number, implementation: P): typeof new implementation<void>((res: any, rej: any) => void);

const bluebirdPromise = wait(1e3, Bluebird);
// typeof bluebirdPromise should be instance of Bluebird

هل سيكون من الممكن الاستعلام عن أنواع المرتجعات بدون typeof ، أم سيكون علينا null as FnType ؟

interface Fn {
    (a: string): string;
    (a: number): boolean;
}
type Ret = Fn(number); // Ret = boolean
type Ret = typeof (null as Fn)(number);

آسف إذا كانت هذه الأسئلة قد تمت الإجابة عليها بالفعل ؛ لم أتمكن من العثور عليهم.

ما هي نقطة new هنا ، ألا تريد فقط typeof implementation() ؟

لا ، لأن implementation() ليس مكالمة صالحة. لا يمكن استدعاء PromiseConstructorLike إلا من خلال new لكل نوع من أنواع الإعلانات. typeof implementation() هو نوع الخطأ ، تمامًا مثل (typeof implementation)['foobar'] سيكون نوع الخطأ.

رابط الملعب

هل من الممكن تقديم أنواع عامة يمكن استنتاجها مثل ما فعله FlowType؟ على الأقل ، يمكنه حل مشكلة الحصول على نوع القيمة المرجعة للوظائف.

type _ExtractReturn<B, F: (...args: any[]) => B> = B;
type ExtractReturn<F> = _ExtractReturn<*, F>;

Cryrivers : راجع # 14400 لمعرفة هذا النهج. إنه لا يحل المشكلة فعليًا حيث يعتمد نوع الإخراج على الإدخال.

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

تمت إضافة عامل تشغيل ReturnType<T> إلى lib.d.ts في TS 2.8 ، مدعوم من الأنواع الشرطية.

نظرًا لأن ReturnType<T> لم يأخذ بعين الاعتبار أنواع الإرجاع التي تعتمد على أنواع الوسيطات ، كمرجع ، هنا تطبيق Flow's $Call النوع.

تحرير: آسف @ goodmind ، لم أكن أدرك أنك مرتبط بالفعل بذلك بالضبط.

لقد قمت بتحديث رسالتي السابقة لحالات استخدام هذا الاقتراح (أو تفسير نداء النوع الخاص به) بناءً على الإضافات الأخيرة لـ TS.
تتم الآن تغطية حالات استخدام مطابقة الأنماط من خلال # 21496 ، مع ترك ... الحالات التي نريد حساب الأنواع بناءً على Lambdas التي يوفرها المستخدم ، على سبيل المثال curry ، تكوين الوظيفة ، map ، reduce ، تعديل العدسات بناءً على لامدا ... الأشياء الممتعة. :)

PS @ thorn0 : أعتقد أن حالة الاستخدام الزاوي الخاصة بك يمكن ملؤها بـ ReturnType (# 21496) الآن!

أعتقد أنه ينبغي تغطية هذا من خلال السماح بهذه التعبيرات.
prop2: typeof this.prop1.big.complex ؛

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

class A {
    x: number;
    static y: number;
    f() {
        const a: number = 1;
        const b: typeof a = 2; // OK
        const c: this.x = 3; // No :(
        const d: this['x'] = 3; // OK
        const e: typeof A.y = 4 // OK
    }
}

NN - يمكنك دائمًا استخدام الأنواع المفهرسة لهذا:

this['x']

@ cameron-martin لا يعمل. ملعب

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

NN فقط استخدم const d: this['x'] = 3;

آه لطيف :)

NN --- أو استخدم

class A {
    x: number;
    static y: number;
    f() {
        const self = this;
        const a: number = 1;
        const b: typeof a = 2; // OK
        const c: typeof self.x = 3; // OK
        const d: typeof self['x'] = 3; // OK
        const e: typeof A.y = 4 // OK
    }
}

tsofist إنني أدرك أن الأعمال المحلية ، لكنني أجد هذا قبيحًا.
إنه مماثل للحفظ اليدوي لرد الاتصال "this" لـ "function () {}" بدلاً من استخدام lambda مع الالتقاط الضمني.

NN

هذا قبيح.

نعم ، هذا مجرد خيار :)

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

RyanCavanaugh أعتقد أنك تعني ReturnType<T> ؟

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

يجب أن تكون قادرًا على كتابته:

type Return1<A1, T extends (a: A1) => any> = T extends (a: A1) => infer R ? R : any;
type Return2<A1, A2, T extends (a: A1, a: A2) => any> = T extends (a: A1, a: A2) => infer R ? R : any;

declare function something(a: number): number;
declare function something(a: string): string;
declare function something(a: number, b: string): boolean;

type A = Return1<number, something>; // number
type B = Return1<string, something>; // string
type C = Return2<number, string, something>; // boolean

لم أختبره رغم ذلك ، وستحتاج إلى مساعد منفصل لكل عدد من args.

ForbesLindesay : something حاليًا متغير على مستوى التعبير - الإشارة إليه على سبيل المثال typeof هنا (أو التصريح عنه كواجهة) يعمل على إصلاح ذلك. أنا لا أتمكن في الواقع من الحصول عليها لتحقيق أنواع عوائد مناسبة على الرغم من (على 2.8.0-dev.20180318 ).

تضمين التغريدة ستختار آلية الاستنتاج طريقة _last_ الزائد:

type Funcs = ((p1: string, p2: string) => void) & ((p1: number) => void);

type FuncPromise1<T> = T extends (p1: infer P1) => void ? (p1: P1) => Promise<[P1]> : never;
type FuncPromise2<T> = T extends (p1: infer P1, p2: infer P2) => void ? (p1: P1, p2: P2) => Promise<[P1, P2]> : never;

let foo: FuncPromise1<Funcs> & FuncPromise2<Funcs>;

image

ومع ذلك ، فإن آلية الاستنتاج _ هي _ قادرة على التعامل مع النقابات tuple:

type Tuples = [string, string] | [number];

type TuplePromise1<T> = T extends [infer P1] ?  (p1: P1) => Promise<[P1]> : never;
type TuplePromise2<T> = T extends [infer P1, infer P2] ? (p1: P1, p2: P2) => Promise<[P1, P2]> : never;

let foo: TuplePromise1<Tuples> & TuplePromise2<Tuples>;

image

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

MeirionHughes :

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

مثل (a: number, b?: string) => boolean -> { a: number, b?: string } ؟ لا يمكننا الحصول على أسماء معلمات بهذا الشكل ، ولكن من الناحية المفاهيمية ، يصبح هذا الأمر أكثر صرامة بالنسبة لمعلمات الراحة ( (a: number, ...b: string[]) => boolean ) ، أيضًا نظرًا لأنه لا يمكننا استخدام أنواع الكائنات للقيام بالطلب.

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

هذا يقلل من مشكلة استخراج الأحمال الزائدة. يجب أن تكون الأحمال الزائدة تقاطعًا لأنواع الوظائف مثل ((a: number) => 123) & ((s: string) => 'hi') ، لذا فإن المشكلة تكمن في كيفية "فك" نوع التقاطع (على سبيل المثال نوع tuple) - حتى الآن ، ليس لدينا ذلك.

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

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

بما أنه تم إغلاق هذه المشكلة الآن ، فهل هناك اقتراح جديد حتى الآن للأجزاء التي لا تزال مفقودة؟

لا شيء أعرفه.

مثل طريقة للتعامل مع نوع العودة اعتمادا على الحجج؟

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

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

type MyType<A> = {
    foo: A
}

type Wrap = {
    <T>(maybe: MyType<T>): MyType<T>;
    (maybe: any): MyType<any>;
}

type Naive = ReturnType<Wrap>; // Naive = { foo: any }
type Proposed1 = Wrap(maybe: number); // Proposed1 = { foo: number }
type Proposed2 = Wrap(maybe: MyType<number>); // Proposed2 = { foo: number }
type Proposed3 = (<T>(maybe: T) => MyType<T>)(maybe: number) // Proposed3 = { foo: number }

حالات الحافة:

const foo = <T>(a: T) => T:

type Edge1 = (typeof foo)(a: number) // Probably trivial?

type Foo = {
    <T>(a: string): T
}

type Edge2 = Foo<number>(a: string) // Should this be allowed? Probably not, because:

type Bar<A> = {
    (a: string): A
}

type Edge3 = Bar<number>(a: string) // Things are getting strange

interface Baz<A> {
    <T>(a: T): T | A
}

type Edge4 = Baz<number>(a: string) // What is this?

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

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

mhegazy تغير موقف الفريق من هذا على الإطلاق ، بالنظر إلى العمل الأخير في # 24897 ؟؟

يبدو أن هناك عددًا غير قليل من المشكلات حول الحلول التي يمكن تقليل حلولها إلى نوع $Call ، وأن النوع $Call سيفتح الباب لطريقة مباشرة نسبيًا لمحاكاة الأنواع ذات النوع الأعلى ؛ راجع https://gist.github.com/hallettj/0fde5bd4c3ce5a5f6d50db6236aaa39e على سبيل المثال (استبدل استخدام $PropertyType و $ObjMap بـ $Call ). تحرير: مثال إضافي: https://github.com/facebook/flow/issues/30#issuecomment -346674472

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

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

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

أوافق على أنه من الممكن القيام بالأشياء باستخدام الأنواع الشرطية. أعتقد أنه لن يجلب الكثير من التعقيد الإضافي في المترجم إذا قمنا بإعادة كتابة User.avatar إلى User extends { avatar: infer T } ? T : never ؟ لذلك على سبيل المثال يمكننا الكتابة

export type Avatar = User extends { avatar: infer T } ? T : never;

كما

export type Avatar = User.avatar;

لتحسين إمكانية القراءة.

المثال الكامل

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

export function findUser() {
  return {
    username: 'johndoe',
    avatar: {
      lg: '1.jpg',
      s: '2.jpg'
    },
    repos: [
      {
        name: 'ts-demo',
        stats: {
          stars: 42,
          forks: 4
        },
        pull_requests: [
          { date: '2019-08-19', tags: ['bug', 'agreed-to-cla'] },
          { date: '2019-08-10', tags: ['bug', 'includes-tests'] },
          { date: '2019-08-07', tags: ['feature'] }
        ]
      }
    ]
  };
}

بفضل الاستدلال من الأنواع المعينة ، يمكننا استخراج النوع من الوظيفة كما يلي:

export type User = ReturnType<typeof findUser>;
export type Avatar = User extends { avatar: infer T } ? T : never;

اقتراح: هذا يجب أن يتم تقييمه لنفس الشيء

export type Avatar = User.avatar;

بالإضافة إلى ذلك ، يمكننا التأكيد على أن User.avatar يجب ألا يكون من النوع never .

مزيد من الأمثلة

export type Repositories = User extends { repos: infer T } ? T : never;
export type Repository = User extends { repos: (infer T)[] } ? T : never;
export type RepositoryStats = Repository extends { stats: infer T } ? T : never;
export type PullRequests = Repository extends { pull_requests: (infer T)[] } ? T : never;
export type PullRequest = Repository extends { pull_requests: (infer T)[] } ? T : never;
export type Tags = PullRequest extends { tags: infer T } ? T : never;
export type Tag = PullRequest extends { tags: (infer T)[] } ? T : never;
export type Repositories = User.repos;
export type Repository = User.repos[];
export type RepositoryStats = User.repos[].stats;
export type PullRequests = User.repos[].pull_requests;
export type PullRequest = User.repos[].pull_requests[];
export type Tags = User.repos[].pull_requests[].tags;
export type Tag = User.repos[].pull_requests[].tags[];

عند تعيين خاصية متداخلة دفعة واحدة ، ليس من الواضح تمامًا ما يحدث

export type Tag2 = User extends { repos: { pull_requests: { tags: (infer T)[] }[] }[] } ? T : never;

هذا من شأنه أن يوضحها كثيرًا

export type Tag = User.repos[].pull_requests[].tags[];

حالة الزاوية

export class Hello {
  static world = 'world';
  world = 42;
}
export type ThisWillBeANumber = Hello extends { world: infer T } ? T : never;
export type ThisWillBeANumber = Hello.world;
export type ThisWillBeAString = (typeof Hello) extends { world: infer T } ? T : never;
export type ThisWillBeAString = (typeof Hello).world;

lukaselmer يبدو أنك تريد فقط

export type Avatar = User["avatar"];

الذي يعمل اليوم

lukaselmer يبدو أنك تريد فقط

export type Avatar = User["avatar"];

الذي يعمل اليوم

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

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

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

interface a {
 foo: bar;
 /* more types */
}

const example = (fooNeeded: [magic] a.foo ) => {};

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

MFry أعتقد أنك تبحث عن بناء الجملة هذا: a['foo']

هل نعرف ما إذا كان هناك حل لهذا حتى الآن؟

أحاول الحصول على شيء مثل هذا:

declare function something<A, B>(): void;

type Payload = string;

const hello = something<{}, Payload>();

declare function doThing<T extends ReturnType<typeof something>>(arg: T): { payload: unknown };

doThing(hello).payload === 123; // this should validate to a string aka type Payload

https://www.typescriptlang.org/play/index.html؟ts=4.0.0-dev.20200512#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXwGccBbEDACy1QHMAeAQQBp4AhAPgAoBKALngDccWYAG4AUGIwBPAA4IAClCkQcUYPAC8hDDCrVxYsHgIZ45EBBWbCJMpRq0A3gF9mi5auCcuB0JFgIKOjYePDAOAAq9nQR8CAAHhggqMAE8ABKZMgwqBGyILTScjiINqQUemycsNR8EbzwjvAySipqfGgA1qg4AO74zr6R0RzmljhcAHQtHmqaGloAjABMAMwi8AD0m -AVaQTkOMgQ6vxQEMJQSbs48FDaujR3nfdFCq2eYkA

مرحبًا maraisr ، لست متأكدًا بنسبة 100٪ مما تحاول تحقيقه. في المثال الخاص بك ، يأخذ something نوعين لكن لا يستخدمهما ، و hello هي القيمة المرجعة لشيء ما سيكون دائمًا void ؛ لذلك لا يرى doThing النوع string $ مطلقًا في أي وقت.

ربما شيء مثل أدناه هو ما تريد؟

declare function something<ReturnType>(): ReturnType;

type Payload = string;

const hello = () => something<Payload>();

declare function doThing<F extends () => any>(f: F): { payload: ReturnType<F> };

doThing(hello).payload === 'a string';

آه نعم - آسف جدًا لذلك. شكرا لك على الاستجابة السريعة !! : 100: @ acutmore

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

شيء مثل:

declare function something<A, B>(a: MyComplexGeneric<A>, b: B[]): { somethingA: number, somethingB: number };

// Those 2 generics influence the return object so they do get used as such. And the 2 arguments are roughly that. Its an object and an array, more-or-less. 

لا تهتم وظيفتي doThing حقًا بما هو العام الأول ( A ) ، لكنها تهتم بما هو الثاني ( B ).

لاحظ أن something في حالة الاستخدام الخاصة بي له تأثير جانبي يمكن قراءته بواسطة doThing .

لذلك لا يمكنني ببساطة الحصول على نوع ReturnType لوظيفة ما - فأنا بحاجة إلى امتصاص النوع العام للوظيفة التي تم إنشاؤها بطريقة ما.


إذا كنت تشعر أن هذا الاستعلام خارج نطاق هذه المشكلة ، فسنواصل رحلتي على StackOverflow!

maraisr شكرا على المعلومات الاضافية.

إذا كنت ترغب في الحصول على doThing لتتمكن من الحصول على النوع الأصلي B من something ، فيجب أن يتم تمريره إلى hello بطريقة ما. يبحث TypeScript فقط عن hello وبدون بعض المساعدة لن يعرف أنه نوع الإرجاع something .

هذه إحدى الطرق التي يمكن من خلالها القيام بذلك:

/** Create a type that can store some extra type information **/
interface SomethingResult<T> {
    __$$__: T;
    somethingA: number;
    somethingB: number;
}

declare function something<A, B>(): SomethingResult<B>;

type Payload = string;

const hello = something<{}, Payload>();

declare function doThing<Result extends SomethingResult<any>>(arg: Result): { payload: Result['__$$__'] };

doThing(hello).payload === 1123; // error because `payload` is of type string
interface User {
  avatar: string;
}

interface UserData {
  someAvatar: User['avatar'];
}

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

أنا أعمل على وظيفة يمكنها تحويل أي استدعاء طريقة إلى إصدار "بدون نقطة" (على سبيل المثال: [].map(() => n > 5) يتحول إلى map(() => n > 5)([]) والشيء الوحيد المفقود هو أن الأنواع الشرطية و infer لا يستطيع unknown .

إذا كان بإمكاني "استدعاء" الوظائف للحصول على النوع ( typeof myFunc(() => Either<string,number>) ) ، فسيكون من الممكن الحصول على هذه الوظيفة (وهذا مستحيل حاليًا) ، وتسهيل القيام بالعديد من الأشياء الأخرى (HKTs ، إلخ .. .)

هل التعقيد مرتفع للغاية لتكون قادرًا على $Call دالة (مثل التدفق)؟ أشعر أن المطبوع عليه بالفعل يفعل ذلك تلقائيًا.

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

تضمين التغريدة شكرًا على الرد ، هل تعلم ما هي المشكلات التي تعقّب حل استدعاء الوظيفة؟

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

يتم تعقب الحالة المحددة لحل تعبير استدعاء في مكان آخر

RyanCavanaugh هل تمانع الارتباط بمكان آخر؟ 🙂

tjjfvi # 37181 يتعلق بشكل أكثر تحديدًا بحل دالة بناءً على مدخلاتها. قد يكون ما تبحث عنه.

acutmore هذا إلى حد ما يتماشى مع ما كنت أبحث عنه ، على الرغم من أنني كنت أتحدث على وجه التحديد عن الأداة المساعدة flow-esque $Call ، أو بناء جملة آخر لأتمكن من تنفيذ مثل هذا. النهج المقترح هناك غريب ولكن شكرا على الرابط.

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

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

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

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

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

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

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