الآن لدينا Mapped Types و keyof ، يمكننا إضافة عامل طرح يعمل فقط على الأنواع الحرفية:
type Omit<T, K extends keyof T> = {
[P in keyof T - K]: T[P];
};
type Overwrite<T, K> = K & {
[P in keyof T - keyof K]: T[P];
};
type Item1 = { a: string, b: number, c: boolean };
type Item2 = { a: number };
type T1 = Omit<Item1, "a"> // { b: number, c: boolean };
type T2 = Overwrite<Item1, Item2> // { a: number, b: number, c: boolean };
تكرار # 4183؟
سأقوم بإعادة تسمية هذا الاقتراح لأن # 4183 لا يحتوي على اقتراح في الواقع.
ahejlsberg تحقق من اقتراحي في # 4183
نأمل أن نرى هذا قريبًا ، سيكون تحسنًا كبيرًا أيضًا!
لاحظ أن keyof T
قد يكون string
(اعتمادًا على المفهرسات المتاحة) ، وبالتالي يجب تحديد سلوك string - 'foo'
، وما إلى ذلك.
تحرير: تصحيح أن keyof T
دائمًا عبارة عن سلسلة ، ومع ذلك تظل نقطة string - 'foo'
.
لاحظ أن keyof T قد يكون سلسلة أو رقمًا أو سلسلة | number (اعتمادًا على المفهرسات المتاحة) ، وبالتالي فإن سلوك السلسلة - "foo" ، والرقم - "1" ، وما إلى ذلك يجب تحديده.
هذا ليس دقيقا. keyof
دائمًا string
.
إذا كانت هناك أي أسئلة حول حالات الاستخدام ، فسيكون هذا مفيدًا بشكل لا يصدق لكتابة Redux ، و Recompose ، ومكتبات مكونات أخرى ذات ترتيب أعلى لـ React. على سبيل المثال ، يؤدي التفاف قائمة منسدلة غير مضبوطة في withState HOC إلى إزالة الحاجة إلى الدعائم isOpen أو toggle ، دون الحاجة إلى تحديد نوع يدويًا.
يلتف () Redux's connect () بالمثل ويوفر بعض / جميع الخاصيات إلى أحد المكونات ، تاركًا مجموعة فرعية من الواجهة الأصلية.
الرجل الفقير Omit
:
type Omit<A, B extends keyof A> = A & {
[P in keyof A & B]: void
}
لا يزال المفتاح "المحذوف" موجودًا ، ولكن غالبًا ما يكون غير قابل للاستخدام ، نظرًا لأن نوعه هو T & void
، ولا شيء عاقل يمكنه تلبية هذا القيد. لن يمنعك من الوصول إلى T & void
، لكنه سيمنعك من تعيينه أو استخدامه كمعامل في دالة.
بمجرد هبوط https://github.com/Microsoft/TypeScript/pull/13470 ، يمكننا أن نفعل ما هو أفضل.
niieani لن يعمل هذا الحل لرد فعل HoC الذي يضخ الدعائم.
إذا استخدمت هذا النمط في "إزالة" الخاصيات ، فسيظل مدقق الحروف يشتكي إذا حذفتها عند استخدام أحد المكونات:
type Omit<A, B extends keyof A> = A & {
[P in keyof A & B]: void
}
class Foo extends React.Component<{a:string, b:string}, void> {
}
type InjectedPops = {a: any}
function injectA<Props extends InjectedPops>(x:React.ComponentClass<Props>):React.ComponentClass<Omit<Props, 'a'>>{
return x as any
}
const Bar = injectA(Foo)
var x = <Bar b=''/>
// Property 'a' is missing in type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<Omit<{ a: string; b: string; }, "a">, Co...'.
أعتقد أنه سيكون من الأسهل إدراك هذا بالشكل التالي:
[P in keyof A - B]
يجب أن يعمل فقط إذا امتد A إلى B ، ويعيد جميع المفاتيح الموجودة في A وليست في B.
type Omit<A extends B, B> = {
[P in keyof A - B]: P[A]
}
type Impossible<A, B> = {
[P in keyof A - B]: string
} /* ERROR: A doesn't extend B */
interface IFoo {
foo: string
}
interface IFooBar extends IFoo {
bar: string
}
type IBar = Omit<IFooBar, IFoo>; // { bar: string }
نظرًا لأنه لا يمكننا تغيير أنواع الحقول عند التمديد ، فإن جميع التناقضات (المعروفة أيضًا باسم السلسلة) - "العالم" ستختفي.
Overwrite
مغطى بالرقم 10727 #. يبدو هذا الاقتراح صيغة بديلة لـ # 13470؟
Overwrite
باستخدام بنية اليوم:
type Item1 = { a: string, b: number, c: boolean };
type Item2 = { a: number };
type ObjHas<Obj extends {}, K extends string> = ({[K in keyof Obj]: '1' } & { [k: string]: '0' })[K];
type Overwrite<K, T> = {[P in keyof T | keyof K]: { 1: T[P], 0: K[P] }[ObjHas<T, P>]};
type T2 = Overwrite<Item1, Item2> // { a: number, b: number, c: boolean };
تحرير: تم إصلاح مشكلة الفهرسة.
لقد جربت يدي أيضًا على Omit
، إذا كان النجاح مختلطًا:
// helpers...
type Obj<T> = { [k: string]: T };
type SafeObj<O extends { [k: string]: any }, Name extends string, Param extends string> = O & Obj<{[K in Name]: Param }>;
type SwitchObj<Param extends string, Name extends string, O extends Obj<any>> = SafeObj<O, Name, Param>[Param];
type Not<T extends string> = SwitchObj<T, 'InvalidNotParam', {
'1': '0';
'0': '1';
}>;
type UnionHas<Union extends string, K extends string> = ({[S in Union]: '1' } & { [k: string]: '0' })[K];
type Obj2Keys<T> = {[K in keyof T]: K } & { [k: string]: never };
// data...
type Item1 = { a: string, b: number, c: boolean };
// okay, Omit, let's go.
type Omit_<T extends { [s: string]: any }, K extends keyof T> =
{[P2 in keyof T]: { 1: Obj2Keys<T>[P2], 0: never }[Not<UnionHas<K, P2>>]}
type T1 = Omit_<Item1, "a">;
// intermediary result: { a: never; b: "b"; c: "c"; }
type T2 = {[P1 in T1[keyof Item1] ]: Item1[P1]}; // { b: number, c: boolean };
// ^ great, the result we want!
رائع ، لقد تم حل مشكلة أخرى!
...
// now let's combine the steps?!
type Omit<T extends { [s: string]: any }, K extends keyof T> =
{[P1 in {[P2 in keyof T]: { 1: Obj2Keys<T>[P2], 0: never }[Not<UnionHas<K, P2>>]}[keyof T] ]: T[P1]};
type T3 = Omit<Item1, "a">;
// ^ yields { a: string, b: number, c: boolean }, not { b: number, c: boolean }
// uh, could we instead do the next step in a separate wrapper type?:
type Omit2<T extends { [s: string]: any }, K extends keyof T> = Omit_<T, K>[keyof T];
// ^ not complete yet, but this is the minimum repro of the part that's going wrong
type T4 = Omit2<Item1, "a">;
// ^ nope, foiled again! 'a'|'b'|'c' instead of 'b'|'c'... wth?
// fails unless this access step is done after the result is calculated, dunno why
لاحظ أن محاولتي لحساب الاختلافات النقابية في # 13470 عانت من نفس المشكلة ... أي من خبراء TypeScript هنا؟ 😄
@ tycho01 ما تفعله هنا مذهل. لقد تلاعبت بها قليلاً ويبدو أن السلوك المكون من خطوة واحدة يبدو وكأنه خطأ في TypeScript. أعتقد أنه إذا قمت بتقديم تقرير خطأ منفصل حول هذا الموضوع ، فيمكننا حله والحصول على خطوة واحدة رائعة Omit
و Overwrite
! :)
niieani : نعم ، القضية مرفوعة في # 16244 الآن.
لقد كتبت قليلاً عن فهمي الحالي / تقدمي في عمليات نوع TS ؛ فقط ضع صورة هنا . كود هنا .
إنه مثل ، يمكننا القيام بعمليات شبيهة بالمنطقية باستخدام السلاسل ، وبالنسبة لعمليات النقابات / الكائنات ، فإن الحدود الحالية تتغلب على خلل الاختلاف هذا.
يبدو أن العمليات على الأساسيات الفعلية غير مطروحة حاليًا ، بينما بالنسبة لأنواع الصفوف لا تزال الأمور صعبة أيضًا - يمكننا الحصول على تمثيلات الاتحاد / الكائنات باستخدام keyof
/ Partial
، ونأمل أن يكون الاختلاف تعمل بهذه الطريقة أيضًا. أقول "نأمل" لأن بعض العمليات مثل keyof
يبدو أنها لا تحب المؤشرات الرقمية ...
أشياء مثل تحويل تلك الأنواع مرة أخرى إلى أنواع tuple ، أو مجرد التدمير المستقيم ...
على مستوى النوع ، ليست ممكنة حتى الآن.
عندما نفعل ذلك ، قد نصل إلى التكرار على مستوى النوع لعمليات شبيهة بالاختزال ، حيث تصبح الأشياء أكثر إثارة للاهتمام من imo.
تحرير: حل مشكلة strictNullChecks
.
@ tycho01 ،
شيء مذهل حقا !!
تلاعبت بها قليلا ما رأيك بهذا الحل؟
type Obj<T> = { [k: string]: T };
type SafeObj<O extends { [k: string]: any }, Name extends string, Param extends string> = O & Obj<{[K in Name]: Param }>;
type SwitchObj<Param extends string, Name extends string, O extends Obj<any>> = SafeObj<O, Name, Param>[Param];
type Not<T extends string> = SwitchObj<T, 'InvalidNotParam', {
'1': '0';
'0': '1';
}>;
type UnionHas<Union extends string, K extends string> = ({[S in Union]: '1' } & { [k: string]: '0' })[K];
type Obj2Keys<T> = {[K in keyof T]: K } & { [k: string]: never };
type Omit_<T extends { [s: string]: any }, K extends keyof T> =
{[P2 in keyof T]: { 1: Obj2Keys<T>[P2], 0: never }[Not<UnionHas<K, P2>>]}
export type Omit<T extends { [s: string]: any }
, K extends keyof T
, T1 extends Omit_<T, K>= Omit_<T, K>
, T1K extends keyof Pick<T1, keyof T>=keyof Pick<T1, keyof T>> =
{[P1 in T1[T1K]]: T[P1]}
;
type Item1 = { a: string, b: number, c: boolean };
const ommited: Omit<Item1, 'a'> = {
b: 6,
c: true,
}
يبدو أن nirendy يعمل من أجلي! 👍
@ tycho01 عمل رائع في حل مشكلة strictNullChecks
.
niieani : يمكنك أن تشكر jaen على ذلك ، انظر # 13470 . 😃
nirendy : قف! لم أكن أدرك أنه يمكننا استخدام بناء جملة الافتراضات هذا من هذا القبيل. يبدو التكرار محرجًا بعض الشيء ، لكن القدرة على حفظ / إعادة استخدام النتائج والتحقق منها لأغراض تصحيح الأخطاء أمر رائع جدًا!
ومع ذلك ... كان فصل الخطوات هنا شيئًا واحدًا ، ولكن أيضًا استبدال [keyof T]
لذلك keyof Pick<>
؟ كيف عرفت شيئًا من هذا ؟! 😆
هذا يدهش يا شباب الكرات ، كيف فعلت ذلك؟
تضمين التغريدة
أشعر أنه لا توجد طريقة لتجنب هذا التكرار هناك (أثناء استخدام أدوات الإصدار الحالي) نظرًا لأنه يلعب دورًا مختلفًا في كل مرة (بمجرد وجوده هناك للتأكد من أن العام يمد بالفعل النوع والآخر موجود من أجل حذف الحاجة إلى توفير جميع المعلمات الجنيسة).
بأي حال من الأحوال ، أعتقد أننا تمكنا من تحقيق شيء مذهل!
لا يهمني حقًا أن الشفرة خرجت بشكل غير ملائم ، سأضعها في ملف إعلان وأستخدمها بطريقة مماثلة لكيفية استخدام أنواع Pick و Partial وما إلى ذلك.
مدهش!
nirendy : نعم ، لا توجد شكاوى هنا! 😃
تحرير: @ aleksey-bykov: بالنسبة لأجزائي على الأقل ، خطوة بخطوة ، من الألف إلى الياء (انظر # 16392).
بعض الأشياء الممتعة هنا. لقد دمجت للتو # 16446 مما يتيح حلاً أبسط:
type Diff<T extends string, U extends string> = ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T];
type Omit<T, K extends keyof T> = { [P in Diff<keyof T, K>]: T[P] };
type Overwrite<T, U> = { [P in Diff<keyof T, keyof U>]: T[P] } & U;
type T1 = Diff<"a" | "b" | "c", "c" | "d">; // "a" | "b"
type Item1 = { a: string, b: number, c: boolean };
type Item2 = { a: number };
type T2 = Omit<Item1, "a"> // { b: number, c: boolean };
type T3 = Overwrite<Item1, Item2> // { a: number, b: number, c: boolean };
النوع Diff
هو عامل طرح فعال لأنواع السلاسل الحرفية. يعتمد على معادلات النوع التالية:
T | never
= T
T & never
= never
(الذي يوفره # 16446)علاوة على ذلك ، فهو يعتمد على حقيقة أنه يمكن فهرسة الكائن الذي يحتوي على توقيع فهرس السلسلة بأي سلسلة.
يجب أن يشتمل عامل الطرح الحرفي الأصلي على علاقات معينة من نوع الترتيب الأعلى. على سبيل المثال ، يجب اعتبار معلمات النوع T
و U
و T - U
نوعًا فرعيًا من T
و { [P in keyof T - U]: T[P] }
يجب اعتباره نوع فائق من T
. لا يحتوي النوع Diff
على هذه ، لذا قد تكون تأكيدات الكتابة ضرورية في بعض الحالات.
لقد لاحظت اختلافًا تقنيًا بسيطًا في أن المنظف Overwrite
تعريف ينتج نوع تقاطع:
type Diff<T extends string, U extends string> = ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T];
type Overwrite<T, U> = { [P in Diff<keyof T, keyof U>]: T[P] } & U;
type Item1 = { a: string, b: number, c: boolean };
type Item2 = { a: number };
type T3 = Overwrite<Item1, Item2> // { b: number, c: boolean } & { a: number }
يبدو أن البديل التالي ، بإضافة خطوة Pick
، يعمل على "إصلاح" ذلك مهما كانت قيمته.
type Overwrite<T, U, Int = { [P in Diff<keyof T, keyof U>]: T[P] } & U> = Pick<Int, keyof Int>;
... على حد علمي يبدو أن هذا لا يزيد عن كونه اختلافًا جماليًا. اوه حسنا!
تحرير: استرجع ذلك ؛ لم أعد أوصي باستخدام هذا الإصدار المستند إلى Pick
. يبدو في الواقع أنه يكسر بعض الأشياء بمهارة مثل محاولاتي لإعادة كتابة Ramda fromPairs
/ zipObject
هنا .
حاولت استخدام هذا (واحد من @ tycho01 و / أو nirendy ، وليس الجديد من ahejlsberg حتى الآن) لحذف خاصية من فئة تتجاوز toString()
، ولم تنجح. هذا مثال بسيط:
type Item1 = { a: string, b: number, c: boolean, toString(): string };
type OmittedItem1 = Omit<Item1, "a">
// OmittedItem1 should be { b: number, c: boolean, toString(): string }, but is {}!
يمكنني أن أتخيل نوعًا ما لماذا تجاوز toString()
والشركة (آه ... valueOf()
؟ ربما جميع طرق النماذج الأولية الأخرى؟) من شأنه أن يفسد أي شيء يعدد المفاتيح ، لكن يبدو أنه أمر مؤسف.
أي شخص يريد التكهن بما إذا كان هذا السلوك المقصود هو Omit
، أو خطأ في Omit
، أو خطأ في Typescript الذي يكسر Omit
؟
jcalz :
نعم ، لقد واجهت مشكلات كبيرة مع toString
(وطريقة Object.prototype
toLocaleString
).
لا أحصل على {}
رغم ذلك - ما هو إصدار TS؟ يبدو أن الملعب والإصدارات الأحدث أفضل.
تحرير: يبدو أن المشكلة تكمن في Diff
: إنها تعمل عن طريق عمل & { [P in U]: never } & { [x: string]: never }
. في حالة toString
الرغم من ذلك ، من المتوقع أن يكون لهذين النوعين من toString
، مما يؤدي إلى إفسادهما. سأحذف toLocaleString
للإيجاز.
بالنسبة إلى الأخير ، يبدو من الممكن أن تقول { toString: "toString"; [k: string]: never }
للكتابة عليه. الشيء الصعب هو ، لا أعتقد أنه يمكنك جعله never
هنا لأنه سيتم استبداله بطريقة النموذج الأولي ...
بالنسبة للأولى ، يبدو أن بناء جملة مثل { toString: "something"; [P in U]: never }
لا يعمل بالنسبة لي ، مما ينتج عنه A computed property name must be of type 'string', 'number', 'symbol', or 'any'
، أو يبدو أن النصف الأخير يتم تجاهله.
سيكون بناء الجملة { [P in keyof U]: never; [K in keyof PrototypeMethods]: "something"; }
مرغوبًا أكثر هنا ولكن مرة أخرى لا نرد. ثم مرة أخرى ، لا يزال السؤال هو ما الذي نكتبه بدلاً من "something"
، لأنك تريد فقط ما تم تحديده بخلاف ذلك ، ولكن في كلتا الحالتين حتى عندما تريد الكتابة فوق toString
كـ never
، لا يمكنك ذلك. بشكل أساسي ، يتم حل جميع العمليات تقريبًا حاليًا toString
.
أفترض أن الجمع بين تراكيب الكائنات هذه لم يكن شائعًا تمامًا ، وقد لا يتم دعم هذه الحالة بالفعل.
... أي تعليق على ذلكahejlsberg؟
ما زلت أفكر في البدائل ... إنه بالتأكيد صعب رغم ذلك.
تحرير 2: هناك مشكلة واحدة هنا هي الغموض في { [k: string]: 123 }['toString']
- يجب أن يكون toString
من بين k
، حل إلى 123
، أو يجب حلها للتو لطريقة النموذج الأولي؟
يفترض TS حاليًا الخيار الأخير ، مما يجعل من الصعب تقديم حالات الاستخدام التي تنوي الأولى.
في الأساس ، أود أن أحاول إنشاء طريقة بديلة للوصول إلى خصائص الكائن المحصنة ضد هذا ، بدلاً من التحقق من المفاتيح الخاصة فقط بدلاً من أساليب النموذج الأولي أيضًا.
لقد أحرزت بعض التقدم ، ولكن من الصعب تحقيق أي شيء دون استخدام عامل الوصول الأصلي للممتلكات ، لذلك يجب أن تتخطى الأمور بعض الشيء لمنع سلوك عربات التي تجرها الدواب من التسلل إلى كل مكان.
لا أحصل على
{}
- ما هو إصدار TS؟ يبدو أن الملعب والإصدارات الأحدث أفضل.
هذا مذهل! يبدو أن إصدار nirendy لا يعمل مع الاختيارات:
type Item1 = { a: string, b?: number, c: boolean }
const omitted: Omit<Item1, 'a'> = { c: true }
// Property 'b' is missing in type '{ c: true; }'.
jcalz : الرد المتأخر ولكن شكرا على التوبيخ. يبدو أن حلها قد لا يكون تافهًا على الرغم من أن الوصول إلى الممتلكات ومعظم العمليات التي تعتمد عليها تتأثر ؛ سيحاول أكثر في نقطة واحدة.
dleavitt :
شكرا لتقرير الشوائب. حاولت قليلا يبدو ، كما لاحظت ، أن Omit
(أي من الإصدارين) لا ينتج حاليًا ?
. باستخدام strictNullChecks
يقوم بدلاً من ذلك بإلحاق | undefined
بالقيمة ، والتي لا تبدو جيدة بما يكفي حتى الآن لإجراء فحص النوع هذا. لست متأكدًا حتى الآن من طريقة لمعالجة هذا الأمر لا يتطلب فحوصات من نوع على مستوى (من المحتمل أن يتم حلها عن طريق على سبيل المثال # 6606) للتحقق مما إذا كان undefined
يفي بخصائص الكائن ...
@ tycho01 هل يمكننا حل مشكلة ?
بكتابة تمرير متعدد؟
بمعنى آخر:
Omit
على النوع غير الاختياري| undefined
إلى المفاتيح الموجودة من النوع المحذوفniieani :
- إعادة إضافة | غير معرف للمفاتيح الموجودة من النوع المحذوف
هذا لا يقطعها في الواقع ؛ باستخدام strictNullChecks
استرجعنا بالفعل { b: number | undefined; c: boolean; }
، لذا يبدو أن ?
مهم.
سأحتاج إلى الاعتراف بأنني لا أفهم تمامًا ما يحدث حتى الآن. إذا كنت تستخدم { [P in keyof Item1]: P }
فإن ?
يظهر على ما يرام ، مما يعني أن المعلومات تظهر في keyof Item1
، على الرغم من أنك إذا قمت بالتحقق من ذلك بنفسك فلن ترى أي معلومات إضافية . يبدو وكأنه خاصية سحرية غير مرئية ، وليس بعد التحقق من الأجزاء الداخلية للمترجم.
2: اخفِ قائمة الخصائص الاختيارية فقط ، بشكل عام ، جانباً
التحقق مما إذا كانت الخاصية اختيارية يمثل تحديًا حاليًا. يبدو أن القيام بـ keyof
لا يعطي هذه المعلومات (بطريقة مرئية) ؛ number | undefined
واضح لنا ، لكن مقدار المشغلين على مستوى النوع محدود للغاية - إذا علمنا أن undefined
يرضي هذا النوع ، فنحن جيدون ، ولكن على مستوى النوع ، كما هو الحال ، ليس لدينا طريقة للقيام بذلك.
// insert `Omit` typing, one or the other depending on whether you use Playground or 2.5
type Item1 = { a: string, b?: number, c: boolean }
type noA = Omit<Item1, 'a'>
// { b: number | undefined; c: boolean; }
// ^ `Omit` degenerates `?` to ` | undefined`
type keys = keyof Item1
// "a" | "b" | "c"
type almost1 = { [P in keyof Item1]: If<ObjectHasKey<noA, P>, (noA & Obj<never>)[P], never> }
// { a: never; b?: number | undefined; c: boolean; }
type almost2 = { [P in keyof Item1]: If<ObjectHasKey<noA, P>, P, never> }
// { a: never; b?: "b"; c: "c"; }
الشيء الذي يجعلنا `` عالقين '' في هذه المرحلة هو أن الطريقة الوحيدة للتخلي عن خاصية a: never
هي عن طريق توصيل مفاتيح almost2
في نفسها ، على الرغم من أن ذلك سيجعلنا في نفس الوقت نخسر b
's ?
الحالة. في almost1
اقتربنا من المكان الذي نريد الذهاب إليه ، لكن لا نقترب من التخلي عن a
بطريقة تترك b
?
سليمة .
في حين أننا لا نستطيع الكشف أو العمل مباشرة على ?
نفسه ، فإن التشغيل عليه بالوكيل من خلال شيكات | undefined
يبدو حلاً عادلاً بدرجة كافية. لكن نعم ، لا توجد فحوصات من النوع على مستوى. بعيدًا عن التصويت على طلب الميزة (أو البدائل ، رأيت مؤخرًا تعليقًا يطلب مستوى النوع instanceof
+ المستوى المنطقي على مستوى النوع) ، لا أعرف.
niieani : بدلاً من ذلك ، قل أن | undefined
يجب اعتباره معادلاً لـ ?
والملف كخطأ. قد يكون هذا مثيرًا للجدل (لست متأكدًا مما إذا كنت سأعتبره أكثر أناقة بنفسي) ، لكنه سيكون أقل انخراطًا في جانب TS ، لذا من المحتمل أن تكون فرص الإصلاح أفضل لأغراضك.
@ tycho01 نعم ، أراه. أعتقد أن فريق TypeScript ذكر في مكان ما أن | undefined
يجب اعتباره معادلاً لـ ?
- أي أنه يتوافق مع أهدافهم. بالنسبة لمعظم المقاصد والأغراض ، هذه هي الطريقة التي يتصرف بها TS حاليًا ، على الرغم من أنني أرى ما تقصده من خلال تمييز المترجم بطريقة ما.
على سبيل المثال ، يعامل Flow هذين ( ?
و | void
) كأشياء مختلفة ، أي يمكنك من الناحية الفنية امتلاك خاصية وتتوقع أن يكون نوعها undefined
. ولكن لأسباب عملية (جميع؟) هم متماثلون إلى حد كبير.
niieani : نعم. يبدو من المناسب تقديم خطأ بعد ذلك.
المعذرة لكوني خارج الموضوع
يمكن محوjcalz عمليًا toString
من ملفات التصريح ، وبهذه الطريقة سيظهر فقط على الفئات / الكائنات التي تنفذها بشكل صريح (بشرط أن يكون التنفيذ الافتراضي عديم الفائدة على أي حال)
هذه هي الطريقة التي يمكن القيام بها:
noLib: true
في tsconfig.json
https://github.com/Microsoft/TypeScript/tree/master/src/lib
وقم بتضمينها في مشروعكtoString
من Object
وقم بالتعليق عليهاinterface Object {
/** The initial value of Object.prototype.constructor is the standard built-in Object constructor. */
constructor: Function;
/** Returns a string representation of an object. */
// toString(): string; <--- HERE
dleavittniieani تقديمه في # 16762.
@ tycho01dleavitt باستخدام Pick
يحفظ اخليارات في كلا الإصدارين من Omit
. لست متأكدًا مما إذا كان هذا يكسر أي شيء آخر.
إليك مثال كامل مع تعريف 2.4 الموجز.
type Diff<T extends string, U extends string> = ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T];
type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>;
interface A {
a: string;
b?: string;
c: number;
}
type B = Omit<A, 'c'>;
const a: A = {
a: '5',
c: 5,
};
const b: B = {
a: '5',
};
// Note: typeof B is (correctly) Pick<A, 'a' | 'b'>
وبالمثل ، يمكنك إعادة تعريف Omit
في النسخة القديمة (باقي التعريف هو _omitted_).
type Omit<T extends { [s: string]: any }
, K extends keyof T
, T1 extends Omit_<T, K>= Omit_<T, K>
, T1K extends keyof Pick<T1, keyof T> = keyof Pick<T1, keyof T>> = Pick<T, T1[T1K]>;
Pinpickle : لقد حاولت قليلاً مع الاختبارات الأخرى التي أجريتها. أرى فقط مشكلات تعريف Omit
الخاصة بك ، ولم يتم العثور على سلبيات! 😃
Pinpickle يعمل هذا أيضًا "بشكل جيد بما فيه الكفاية" مع تجاوز toString()
، أو على الأقل بشكل قابل للحل. ("الحل البديل" كلمة ، أليس كذلك؟)
type Item1 = { a: string, b: number, c: boolean, toString(): string };
type OmittedItem1 = Omit<Item1, "a">; // { b: number, c: boolean } almost?
type OmittedItem1Fixed = OmittedItem1 & { toString(): string }; // good enough
تم تذكيرني بتعليق في # 12424 ، لا تزال عمليات التنفيذ الحالية Omit
و Overwrite
تفشل في إعادة إنشاء أي فهرس سلسلة قد يكون لدى أنواع الكائنات المصدر. يمكن التحقق من وجود الفهرس باستخدام # 6606.
ahejlsbergPinpickle وغيرها - بفضل بمساعدة على هذا! باستخدام التقنية المذكورة أعلاه ، تمكنا من تكرار نوع التدفق $ Diff . بالتعامل بشكل صحيح مع defaultProps
في React :
/**
* From https://github.com/Microsoft/TypeScript/issues/12215#issuecomment-307871458
* The Diff type is a subtraction operator for string literal types. It relies on:
* - T | never = T
* - T & never = never
* - An object with a string index signature can be indexed with any string.
*/
export type StringDiff<T extends string, U extends string> = ({[K in T]: K} &
{[K in U]: never} & {[K: string]: never})[T];
/**
* From https://github.com/Microsoft/TypeScript/issues/12215#issuecomment-311923766
* Omits keys in K from object type T
*/
export type ObjectOmit<T extends object, K extends keyof T> = Pick<T, StringDiff<keyof T, K>>;
/**
* Returns a version of type T where all properties which are also in U are optionalized.
* Useful for makding props with defaults optional in React components.
* Compare to flow's $Diff<> type: https://flow.org/en/docs/types/utilities/#toc-diff
*/
export type ObjectDiff<T extends object, U extends object> = ObjectOmit<T, keyof U & keyof T> &
{[K in (keyof U & keyof T)]?: T[K]};
طرح سؤال محذوف قبل تبديل الدماغ إلى وضع جافا سكريبت :-) (آسف على البريد العشوائي)
@ rob3c :
type OmitFromBoth<T, K extends keyof T> = Omit<T & typeof T, K | 'prototype'>;
type WorksForBoth = OmitFromBoth<Hmm, 'I1' | 'S1'>;
// "I1" | "S2"
مثل هذا؟ لم تختبر ، لكنك أردت فقط اختصار مثالك الأخير ، أليس كذلك؟
@ tycho01 لقد حاولت ذلك بالفعل بالإضافة إلى الاختلافات الأخرى ، لكن لم ينجح أي منها. لا يبدو أن هناك طريقة للإشارة إلى النموذج الأولي لـ T ضمن التعريف العام. ربما يرجع ذلك إلى عدم سماح جافا سكريبت بالوصول إلى خصائص النموذج الأولي "الثابتة" من مثيل (على عكس جافا ، على سبيل المثال). لهذا السبب حذفت السؤال ، لكنك أجبت بسرعة :-)
@ rob3c : إذن لا يوجد typeof
على T
هاه؟ أرى ما ستحصل عليه في ذلك الوقت ؛ لم أجرب مثل هذه الأشياء حقًا حتى الآن. سوف أعترف أنني لست على علم بالحلول البديلة في هذه الحالة. أكتشف ببطء أن التصحيح باستخدام المترجم أقل سوءًا مما كنت أعتقد ، لكنه لا يزال غير رائع.
robc هناك طرق للوصول إلى prototype
في سياق عام (أشعر بالفضول الآن حول ماهية السؤال 😁).
تم إصلاح الخطأ المطبعي
aluanhaddad كان السؤال أساسًا حول كتابة إصدار Omit
أعلاه والذي يُرجع الخصائص المفلترة keyof
لكل من المثيل والأعضاء الساكنين من النوع بينما تحتاج فقط إلى كتابة Omit<MyClass, ...>
وليس Omit<MyClass & typeof MyClass, ...>
. كنت أقول prototype
أعلاه ، لكن كان من المفترض أن يكون constructor
، حيث أن الأعضاء "الثابتون" عالقون بواسطة الكتابة المطبوعة (أعتقد أنني قد تشتت انتباهي بمحاولة تصفية "النموذج الأولي" الممتلكات في رمز بلدي والمثال). ولأسباب مختلفة موثقة في مكان آخر ، تتم كتابة الخاصية constructor
فقط على أنها Function
، لذا فإن جميع الأشكال التي جربتها والتي سحبت المفاتيح من عضو 'constructor' نتج عنها فقط تلك الموجودة على Function
بدون الدعائم الثابتة المعلنة في الفصل.
على أي حال ، على الرغم من كونها مثيرة للاهتمام من حيث الخلاف العام ، إلا أنها غير ذات صلة في حالتي الخاصة ، نظرًا لأنه لا يمكنك الوصول مباشرة إلى الأعضاء الثابتة من مثيل على أي حال في جافا سكريبت لول (ألومها على عمى اللغة اللحظي بسبب عدم كفاية القهوة).
كان الرمز الأصلي شيئًا من هذا القبيل:
type Diff<T extends string, U extends string> = (
& { [P in T]: P }
& { [P in U]: never }
& { [x: string]: never }
)[T];
type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>;
class Hmm {
readonly I1 = 'I1';
readonly I2 = 'I2';
static readonly S1 = 'S1';
static readonly S2 = 'S2';
};
// "I2"
type WorksForInstancePropsOnly = base.Omit<Hmm, 'I1'>;
/// "prototype" | "S1" | "S2"
type HasAnnoyingPrototypeKey = keyof typeof Hmm;
// "S2"
type WorksForStaticPropsOnly = base.Omit<typeof Hmm, 'S1' | 'prototype'>;
// "I1" | "S2"
type WorksForBoth = base.Omit<Hmm & typeof Hmm, 'I1' | 'S1' | 'prototype'>;
type OmitForBoth<T, ...> = ???
@ rob3c
على أي حال ، على الرغم من كونها مثيرة للاهتمام من حيث الخلاف العام ، إلا أنها غير ذات صلة في حالتي الخاصة ، نظرًا لأنه لا يمكنك الوصول مباشرة إلى الأعضاء الثابتة من مثيل على أي حال في جافا سكريبت لول (ألومها على عمى اللغة اللحظي بسبب عدم كفاية القهوة).
هذا البيان غير دقيق.
من الممكن أفق
class Hmm {
static s1 = 'static one'
static s2 = 'static two'
i1 = 'instance one'
i2 = 'instance two'
}
const foo = new Hmm()
console.log(foo)
const {s1, s2} = foo.constructor
console.log({s1, s2})
تضمين التغريدة
هذا البيان غير دقيق.
في الواقع، بياني دقيق، ولكن قد تكون لديكم يساء فهمها المصطلحات بلدي :-)
على عكس Java ، على سبيل المثال ، لا يمكنك الوصول مباشرة إلى الأعضاء الثابتة في مثيل (على سبيل المثال myInstance.myStaticProp
). يوضح المثال الخاص بك محاولة للوصول غير المباشر عبر خاصية constructor
للمثيل - والتي ستشتكي من الكتابة المطبوعة مع تمايل الأحمر المقابل في vscode ، لأن هذا متاح فقط في وقت التشغيل. لهذا السبب أسفت على كتابة constructor
فقط على أنه Function
أعلاه ، بدلاً من شيء مكتوب بقوة أكبر (راجع https://github.com/Microsoft/TypeScript/issues/3841 وما يتصل بها).
على أي حال ، يتعلق السؤال بتعريفات النوع العام باستخدام keyof
وما شابه ذلك لأغراض التحسس المكتوب بشدة والتحقق الشامل من النوع أثناء كتابة التعليمات البرمجية ، وليس ما هو متاح بالفعل في وقت التشغيل. نظرًا لأن القصد من استخدامي لـ keyof
كان لكتابة الخصائص المتاحة التي يمكن الوصول إليها مباشرة والمعلن عنها بشدة ، فإن توفرها على constructor
في وقت التشغيل غير ذي صلة (كما هو مذكور أعلاه).
حتى فيما يتعلق بالسؤال النظري فقط حول ما إذا كان من الممكن دمج كليهما في تعريف نوع عام باستخدام T
، فإن الإجابة هي لا بدون كتابة constructor
صراحة على فئات الاهتمام ، حيث إنها فقط كتب Function
بشكل عام.
أحاول استخدام نوع الحذف كما اقترحه في هذا التعليق :
export type RegionsTableProps =
ObjectOmit<TableProps, 'headerHeight' | 'rowHeight' | 'rowCount'> &
{
regions: Region[];
onRegionClick: (id: string) => void;
};
class RegionsTable extends React.PureComponent<RegionsTableProps> {}
حيث يكون TableProps
من @ types / response-virtualized ( width?: number;
)
ومع ذلك ، عندما أحاول استخدام RegionTable مثل هذا:
table = ({ width, height }: Dimensions) => (
<RegionsTable
regions={this.props.regions.list}
onRegionClick={this.onRegionClick}
width={width}
height={height}
/>
);
لدي خطأ:
TS2339: Property 'width' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<RegionsTable> & Readonly<{ children?: ReactNode; }...'.
إذا استبدلت ObjectOmit<TableProps, 'headerHeight' | 'rowHeight' | 'rowCount'>
بـ Partial<TableProps>
تجميع كل شيء.
لا أفهم تمامًا ما يجري وكيفية إصلاح ذلك. أي شخص لديه أي أفكار؟
doomsower ربما يرجع ذلك إلى [key: string]: any
في GridCoreProps
// A = never
export type A = StringDiff<keyof TableProps, 'headerHeight' | 'rowHeight' | 'rowCount'>
(TS 2.4.2)
ahejlsberg Diff
، Omit
و Overwrite
هي رائعة ، هل يمكننا أن نتوقع منهم أن يدخلوا المكتبة القياسية في وقت ما إلى جانب Record
، Partial
، Readonly
، Pick
، وما إلى ذلك؟
مقابل Overwrite
، ما هو الإصدار المفضل؟
للمهتمين ، لقد أضفت Diff
، Omit
و Overwrite
إلى Type Zoo .
يوجد pelotom بالفعل https://github.com/gcanti/typelevel-ts و https://github.com/tycho01/typical
goodmind لمزيد من
متفق عليه ، بينما لم نستقر حتى الآن على أفضل الممارسات الصحيحة ، فإننا بالتأكيد بحاجة إلى مزيد من المنافسة. لا تزال أنواع libs تواجه مشكلات كبيرة في الاختبار على سبيل المثال الأنواع غير المنتهية (عادةً الأنواع العودية التي تم التنصت عليها) - بدلاً من وجود اختبار وحدة واحدة يفشل التجميع الكامل الخاص بك فقط في الإنهاء. أود أن أرى المزيد من الناس يحاولون أن يأخذوا الأمور بأنفسهم على أي مستوى.
لقد لاحظت مشكلة في Omit
والوظائف العامة ، أو على الأقل في إحدى عمليات التنفيذ. لست متأكدًا تمامًا من كيفية وصفه ، لذلك سألتزم بنموذج رمز:
type Diff<T extends string, U extends string> = ({ [P in T]: P } &
{ [P in U]: never } & { [x: string]: never })[T];
type Omit<T, K extends keyof T> = { [P in Diff<keyof T, K>]: T[P] };
interface BaseType {
x: string;
}
const fnWithDefault = <T extends BaseType>(
input: Omit<T, keyof BaseType>
): T => {
// Error: Spread types may only be created from object types
const attempt1 = { ...input, x: 'foo' };
// Error: Type 'Omit<T, "x"> & { x: string; }' is not assignable to type 'T'.
return Object.assign({}, input, { x: 'foo' });
};
fnWithDefault<{ x: string; y: string }>({ y: 'bar' });
fnWithDefault
هي وظيفة تضيف x
إلى النتيجة ، وتعيد T
، وبالتالي يجب أن يكون نوع الإدخال T
بدون x
. على الرغم من ذلك ، لا يبدو أن TypeScript يسمح بهذا مع أي من السبريد أو Object.assign
.
بشكل أساسي ، أتوقع أن يتم Omit<T, 'x'> & { x: string }
إلى T
. (بافتراض أن T.x
عبارة عن سلسلة نصية). لست متأكدًا مما إذا كانت هذه مشكلة في TypeScript ، أو في تنفيذ Omit
، أو حتى حسب التصميم.
العودة لبعض الوقت إلى عمليات التنفيذ Omit
. لقد أنشأت مثالًا مبسطًا بتطبيقات متنوعة Omit
. ثم أختبرها على الواجهة مع الحقول الاختيارية والقراءة فقط. أحدهم ، كما ذكر سابقًا ، لا يحترم الحقول الاختيارية. لذلك ، اختبرت ذلك مرة أخرى ، واستبدلت أجزاء من Omit
بتطبيقات أساسية. انظر إلى الكود أدناه:
// built-in Pick definition for reference
// type Pick<T, K extends keyof T> = {[P in K]: T[P]; };
// well-known Diff implementation
type Diff<T extends string, U extends string> = ({[P in T]: P } & {[P in U]: never } & { [x: string]: never })[T];
// our example interface with some optional fields
interface Example {
a?: number;
readonly b: boolean;
c?: string;
readonly d: any;
}
// Omit implementation which doesn't respect optional fields and readonly
type Omit<T, K extends keyof T> = {[P in Diff<keyof T, K>]: T[P]};
type ExampleSubsetA = Omit<Example, 'a' | 'b'>;
// ExampleSubsetA = { c: string; d: any; }
// Omit implementation using Pick, which works good with optional fields and readonly
type OmitUsingPick<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>;
type ExampleSubsetB = OmitUsingPick<Example, 'a' | 'b'>;
// ExampleSubsetB = { c?: string; readonly d: any; }
// ok, so we now substitute Diff with its body, resulting in
// Omit implementation with Pick but without Diff, that still works good with optional fields and readonly
type OmitUsingPickWithoutDiff<T, K extends keyof T> = Pick<T, ({[P in keyof T]: P } & {[P in K]: never } & { [x: string]: never })[keyof T]>;
type ExampleSubsetC = OmitUsingPickWithoutDiff<Example, 'a' | 'b'>;
// ExampleSubsetC = { c?: string; readonly d: any; }
// well, so now we substitute Pick with its body resulting in
// Omit implementation without any other interfaces, which suddenly stopped respecting optional fields and readonly
type OmitSelfsufficient<T, K extends keyof T> = {[P in ({[P in keyof T]: P } & {[P in K]: never } & { [x: string]: never })[keyof T]]: T[P]};
type ExampleSubsetD = OmitSelfsufficient<Example, 'a' | 'b'>;
// ExampleSubsetD = { c: string; d: any; }
( كود على TypeScript Playground )
أسئلتي هي ، لماذا التخلص من نتائج Pick من المعدلات الاختيارية والقراءة فقط التي يتم فقدانها في العملية؟ بالنسبة لي يبدو أن هناك بعض الأخطاء في TypeScript نفسها.
يبدو أن هناك تغييرًا أخيرًا في Diff
و Omit
:
type Diff<T extends string, U extends string> = ({[P in T]: P } & {[P in U]: never } & { [x: string]: never })[T]
type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>
لقد كان يعمل منذ فترة ، ولكنه الآن أخطاء (باستخدام dtslint الذي يسحب أحدث typescript@next
ERROR: 2:45 expect TypeScript<strong i="13">@next</strong> compile error:
Type '({ [P in T]: P; } & { [P in U]: never; } & { [x: string]: never; })[keyof T]' does not satisfy the constraint 'keyof T'.
Type '({ [P in T]: P; } & { [P in U]: never; })[keyof T]' is not assignable to type 'keyof T'.
لقد جربت العديد من تطبيقات Omit
المدرجة بواسطة aczekajski وحصلت على العديد من الأخطاء:
type Omit<T, K extends keyof T> = {[P in Diff<keyof T, K>]: T[P]};
ERROR: 1:44 expect TypeScript<strong i="21">@next</strong> compile error:
Type '({ [P in T]: P; } & { [P in U]: never; } & { [x: string]: never; })[keyof T]' is not assignable to type 'string'.
Type '({ [P in T]: P; } & { [P in U]: never; })[keyof T]' is not assignable to type 'string'.
ERROR: 1:63 expect TypeScript<strong i="22">@next</strong> compile error:
Type 'P' cannot be used to index type 'T'.
md5-b82dbd8360a59e843eda22c0d24ea876
خطأ: 1:54 توقع خطأ ترجمة TypeScript @ التالي :
اكتب '({[P in T]: P؛} & {[P in U]: never؛} & {[x: string]: never؛}) [keyof T]' لا يفي بالقيد 'keyof T' .
اكتب '({[P in T]: P؛} & {[P in U]: never؛}) [keyof T]' غير قابل للتخصيص لكتابة 'keyof T'.
```ts
type OmitUsingPickWithoutDiff<T, K extends keyof T> = Pick<T, ({[P in keyof T]: P } & {[P in K]: never } & { [x: string]: never })[keyof T]>
ERROR: 1:65 expect TypeScript<strong i="31">@next</strong> compile error:
Type '({ [P in keyof T]: P; } & { [P in K]: never; } & { [x: string]: never; })[keyof T]' does not satisfy the constraint 'keyof T'.
Type '({ [P in keyof T]: P; } & { [P in K]: never; })[keyof T]' is not assignable to type 'keyof T'.
md5-220859cd669f13e25d02f34222d801f3
خطأ: 1:58 توقع خطأ ترجمة TypeScript @ التالي :
اكتب '({[P in keyof T]: P؛} & {[P in K]: never؛} & {[x: string]: never؛}) [keyof T]' غير قابل للتخصيص لكتابة 'string' .
اكتب '({[P in keyof T]: P؛} & {[P in K]: never؛}) [keyof T]' غير قابل للتخصيص لكتابة 'string'.
الخطأ: 1: 138 توقع خطأ ترجمة TypeScript @ التالي :
لا يمكن استخدام النوع "P" لفهرسة النوع "T".
""
قد يكون هذا العلاقات العامة قد كسرها: https://github.com/Microsoft/TypeScript/pull/17912
في حين أن السلوك المتوقع Omit
تم كسره بشكل واضح في next الحالي ، يجب أن يدرك الشخص الذي يرغب في إصلاحه أنه كما
إصلاح @ aleksey-bykov يصل إلى # 21156.
aczekajski نحن على دراية بالسلوك الغريب ، وهو ناتج عن تفاعل عدد قليل من الأنظمة غير السليمة تمامًا. أنا أعتبر هذه الأنواع "استخدام على مسؤوليتك الخاصة".
أعتقد أن النوع Spread
في # 21316 هو في الأساس نسخة ممتازة من Overwrite
هنا.
التعليق هنا للأشخاص الذين وصلوا إلى صفحة هذا العدد من النت بعد إصدار 2.8.
مع TS 2.8 الطريقة الأكثر ملاءمة لتنفيذ Omit
الآن أدناه ، مع الاستفادة من الأنواع الشرطية والنوع المدمج الجديد Exclude
:
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
إلى جانب هذه الأسطر ، إليك تنفيذ Overwrite
:
type Overwrite<T1, T2> = {
[P in Exclude<keyof T1, keyof T2>]: T1[P]
} & T2;
أو بمجرد أن يكون لديك Omit
،
type Overwrite<T, U> = Omit<T, Extract<keyof T, keyof U>> & U;
نظرًا لوجود Omit
جديد وأفضل ، كنت آمل أن أفعل شيئًا بدا مستحيلًا مع إصدارات TS السابقة. لنفترض أن لدي واجهات من هذا القبيل:
interface P1 {
type: 1,
a: string;
}
interface P2 {
type: 2,
b: string;
}
type P = { id: number } & (P1 | P2);
ما أريد تحقيقه هو التخلص من حقل id
ولا يزال لدي اتحاد تمييزي. ومع ذلك ، عندما أقوم بعمل Omit<P, 'id'>
ما أحصل عليه لم يعد اتحادًا مميّزًا ، تختفي الحقول a
و b
أثناء مرحلة Exclude
.
الأمر المثير للاهتمام ، ببساطة إعادة كتابة نوع P
بشيء من هذا القبيل:
type Rewrite<T> { [K in keyof T]: T[K] };
لن يكسر الاتحاد. لذلك فكرت في استخدام الأنواع الشرطية لحذف جزء "الاستبعاد". لذلك كتبت هذا:
type OmitFromUnion<T, X extends keyof T> = { [K in keyof T]: (K extends X ? never : T[K]) };
المنطق في ذهني وراء هذا: إذا كانت إعادة الكتابة يمكن أن تحافظ على اتحاد مميز ، فلنقم بذلك ولكن إذا كان هذا K
هو ما نريد حذفه ، اجعله never
(بدلاً من استبعاد هذا المفتاح في جزء رسم الخرائط). فشل هذا فشلاً ذريعًا ، ولم يفعل شيئًا بشكل فعال (أنتج اتحادًا تمييزيًا ولكن لا يزال مع هذا الحقل id
فيه).
هل لا يزال من المستحيل حتى يكون هناك طريقة للحصول على جميع المفاتيح الممكنة للنقابات التمييزية (لجميع الحالات)؟
aczekajski إذا كنت تريد توزيع Omit
على الاتحادات ، فيمكنك تحديده على النحو التالي :
type Omit<T, K extends keyof T> = T extends any ? Pick<T, Exclude<keyof T, K>> : never
الذي يستخدم حقيقة أن الأنواع الشرطية توزع تلقائيًا T
في الحالة أعلاه).
يمكنك التحقق من أن ما ورد أعلاه Omit<P, 'id'>
يتصرف مثل P1 | P2
.
jcalz حسنًا ، عند قراءة المستندات ، فاتني تمامًا حقيقة أنه يتم توزيعها تلقائيًا على النقابات. في البداية ، يبدو أن هذا T extends any
هذا صحيح دائمًا وعلى هذا النحو لا يفعل شيئًا ، لذلك لا أحب حقًا "شعور القرصنة" حول هذا الحل ، ولكن القديم Diff
كان أكثر من ذلك من القرصنة.
تلخيصًا ، أليس الحذف المذكور في التعليق أعلاه هو الأفضل من الحل السابق الذي يفرمل النقابات؟
بالمناسبة ، من الممكن الآن الحصول على جميع المفاتيح لجميع حالات النقابات التي تم التمييز ضدها من:
type AllPossibleKeys<T> = T extends any ? keyof T : never;
هل هناك طريقة مع 2.8 Exclude
وما إلى ذلك لمنع بصق الخصائص الاختيارية مثل type | undefined
هنا؟
type Overwrite<T1, T2> = { [P in Exclude<keyof T1, keyof T2>]: T1[P] } & T2;
type A = Overwrite<{ myOptional?: string, myRequired: number }, { myRequired: string }>;
// compiler complains Property 'myOptional' is missing in type '{ myRequired: string; }'
let x: A = {
myRequired: 'hello'
};
@ drew-r لا يمكنك فقط استخدام NonNullable
@ drew-r جرب هذا الحل: https://github.com/Microsoft/TypeScript/issues/12215#issuecomment -378367303
@ drew-r استخدم هذا مقابل Overwrite
:
type Overwrite<T1, T2> = Pick<T1, Exclude<keyof T1, keyof T2>> & T2
Pick
بشكل خاص من قبل المترجم وسيحتفظ بالمعدلات في النوع الأصلي ، بدلاً من تحويلها ، مثل الأنواع المعينة متجانسة الشكل.
ferdaber ، شكرًا على هذا
يتم التعامل مع Pick بشكل خاص بواسطة المترجم وسيحتفظ بالمعدلات في النوع الأصلي ، بدلاً من تحويلها ، مثل الأنواع المعينة متجانسة الشكل.
يمكن العثور على مزيد من المعلومات في المستندات :
القراءة فقط والجزئية والاختيار متماثلان في حين أن التسجيل ليس كذلك. أحد الأدلة على أن السجل ليس متماثلًا هو أنه لا يتطلب نوع إدخال لنسخ الخصائص من:
type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>
تعمل الأنواع غير المتجانسة بشكل أساسي على إنشاء خصائص جديدة ، لذا لا يمكنها نسخ معدّلات الخصائص من أي مكان.
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
أعلم أن هذا قد تم حذفه من قاعدة كود TypeScript ، لكنني أرى نفسي وزملائي بحاجة إلى هذا بشكل أسبوعي تقريبًا وفي كل مشروع جديد. نحن نستخدمه فقط _ كثيرًا _ ولأنه يشبه "عكس" Pick
أشعر أكثر فأكثر أن هذا يجب أن يتم شحنه مع TypeScript افتراضيًا. من الصعب بشكل خاص على مطوري TS الجدد الذين يرون Pick
ويبحثون عن Omit
ولا يتعلق الأمر الآن بخيط GitHub هذا.
هل توجد طريقة لإزالة نوع عريض من نوع توحيد بدون إزالة الأنواع الفرعية أيضًا؟
export interface JSONSchema4 {
id?: string
$ref?: string
// to allow third party extensions
[k: string]: any
}
type KnownProperties = Exclude<keyof JSONSchema4, string | number>
// I want to end up with
type KnownProperties = 'id' | 'ref'
// But, somewhat understandably, get this
type KnownProperties = never
// yet it seem so very within reach
type Keys = keyof JSONSchema4 // string | number | 'id' | 'ref'
جرب هذا:
type KnownKeys<T> = {
[K in keyof T]: string extends K ? never : number extends K ? never : K
} extends {[_ in keyof T]: infer U} ? U : never;
أنا عليه ، كابتن!
ferdaber ، لقد
هل هذا يعتمد إلى حد ما على الاختراق الأصلي Diff
؟
لم أفكر حتى في تجربة الأنواع الشرطية هنا.
أرى أنه يستند إلى حقيقة أن string
يمتد string
(تمامًا كما 'a'
يمتد string
) لكن string
لا يمتد 'a'
، وبالمثل للأرقام.
أولاً ، يقوم بإنشاء نوع معين ، حيث تكون القيمة لكل مفتاح من T:
بعد ذلك ، تحصل بشكل أساسي على valueof
للحصول على اتحاد لجميع القيم:
type ValuesOf<T> = T extends { [_ in keyof T]: infer U } ? U : never
""
اختبار الواجهة {
req: سلسلة
opt: سلسلة
}
اكتب النصف الأول
}
اكتب ValuesOf
// أو ما يعادله ، نظرًا لأن T هنا ، و T في النصف الأول لهما نفس المفاتيح ،
// يمكننا استخدام T من FirstHalf بدلاً من ذلك:
اكتب SecondHalf
اكتب أ = النصف الأول
//انتاج:
اكتب أ = {
[x: string]: مطلقًا ؛
req: "req" ؛
اختيار ؟: "اختيار" | غير معرف؛
}
اكتب a2 = ValuesOf // "req" |
// "req" |
// استبدال ، لإنشاء تعريف نوع واحد ، نحصل على حل ferdaber :
اكتب KnownKeys
} يمتد {[_ in keyof T]: استنتاج U}؟ U: أبدا؛
// النوع ب = المفاتيح المعروفة
ferdaber هذا مذهل. تكمن الحيلة في كيفية عمل infer
... يبدو أنه يتكرر عبر جميع المفاتيح ، سواء المفاتيح "المعروفة" (التي أسميها "الحرفية") ومفاتيح الفهرس ، ثم تعطي اتحاد النتائج . هذا يختلف عن عمل T[keyof T]
الذي ينتهي فقط باستخراج توقيع الفهرس. عمل جيد جدا.
@ qm3ster. يمكنك بالفعل التمييز بين المفاتيح الاختيارية والمفاتيح المطلوبة التي قد تكون قيمها undefined
:
type RequiredKnownKeys<T> = {
[K in keyof T]: {} extends Pick<T, K> ? never : K
} extends { [_ in keyof T]: infer U } ? ({} extends U ? never : U) : never
type OptionalKnownKeys<T> = {
[K in keyof T]: string extends K ? never : number extends K ? never : {} extends Pick<T, K> ? K : never
} extends { [_ in keyof T]: infer U } ? ({} extends U ? never : U) : never
التي تنتج
type c = RequiredKnownKeys<test> // 'reqButUndefined' | 'req'
type d = OptionalKnownKeys<test> // 'opt'
كل الفضل يعود إلىajafff! https://github.com/Microsoft/TypeScript/issues/25987#issuecomment -408339599
تضمين التغريدة
{} extends Pick<T, K>
لماذا لم أفعل!
أنتم مجموعة من المعجبين هنا أو شيء من هذا القبيل؟
ferdaber بعد فوات الأوان ، لقد
لا يمر عمل جيد بلا عقاب.
مثل donaldpipowitch المشار إليه أعلاه ، هل يمكننا رجاءًا الحصول على Omit
في TypeScript أيضًا. أنواع المساعد الحالي مثل Exclude
و Pick
مفيدة للغاية. أعتقد أن Omit
هو أيضًا شيء مفيد جدًا. يمكننا دائمًا إنشاءها بأنفسنا
اكتب حذف
= انتقاء >
لكن وجوده مدمجًا بدلاً من الاضطرار دائمًا إلى البحث عن هذا النوع سيكون أمرًا رائعًا للغاية! يمكنني دائمًا فتح عدد جديد لمناقشة هذا الأمر بشكل أكبر.
نوع المساعد Omit
يحظى بدعم رسمي بدءًا من TypeScript 3.5
https://devblogs.microsoft.com/typescript/announcing-typescript-3-5/#the -omit-helper-type
التعليق الأكثر فائدة
بعض الأشياء الممتعة هنا. لقد دمجت للتو # 16446 مما يتيح حلاً أبسط:
النوع
Diff
هو عامل طرح فعال لأنواع السلاسل الحرفية. يعتمد على معادلات النوع التالية:T | never
=T
T & never
=never
(الذي يوفره # 16446)علاوة على ذلك ، فهو يعتمد على حقيقة أنه يمكن فهرسة الكائن الذي يحتوي على توقيع فهرس السلسلة بأي سلسلة.
يجب أن يشتمل عامل الطرح الحرفي الأصلي على علاقات معينة من نوع الترتيب الأعلى. على سبيل المثال ، يجب اعتبار معلمات النوع
T
وU
وT - U
نوعًا فرعيًا منT
و{ [P in keyof T - U]: T[P] }
يجب اعتباره نوع فائق منT
. لا يحتوي النوعDiff
على هذه ، لذا قد تكون تأكيدات الكتابة ضرورية في بعض الحالات.