تايب سكريبت - كيف يمكن توسيع نوع keyof بحيث يشمل النسخ المعدلة من المفاتيح، على سبيل المثال التي تبدأ بـ '-'؟
على سبيل المثال، أرغب في إضافة أمان النوع في Typescript إلى حل فرز مصفوفة الكائنات في Javascript العادية حسب قيمة خاصية السلسلة. يقبل كوسائط مفاتيح الكائن المراد فرزه، لتحديد المفتاح الذي سيتم الفرز بحسبه. إذا كان المفتاح مسبوقًا بـ -، يتم عكس الفرز.
كيف يمكنني كتابة النوع ليقبل، على سبيل المثال، كل من "age" و "-age"؟
هذه محاولتي:
export function dynamicSortMultiple<T extends object, U extends keyof T>(
props: Array<U>,
) {
function dynamicSort(key: U) {
let sortOrder = 1
if (typeof key === 'string' && key.startsWith('-')) {
sortOrder = -1
}
return function (a: T, b: T) {
const result = a[key] < b[key] ? -1 : a[key] > b[key] ? 1 : 0
return result * sortOrder
}
}
return function (obj1: T, obj2: T) {
let i = 0
let result = 0
const numberOfProperties = props?.length
while (result === 0 && i < numberOfProperties) {
result = dynamicSort(props[i])(obj1, obj2)
i++
}
return result
}
}
export interface User {
userId: string
firstName: string
lastName: string
login: string
password: string
rank: number
score: number
age?: number
}
dynamicSortMultiple<User, keyof User>(['firstName', 'score', '-age'])
ملعب تايب سكريبت
في السطر الأخير أرى الخطأ
Type '"-age"' is not assignable to type 'keyof User'.
هل توجد أي طريقة لتمديد keyof User بشكل صحيح بحيث تظل القيم المسبوقة بـ '-' تُعتبر قيمًا صالحة لهذا النوع؟
سأقدّر أي حل حتى لو استبدلت بحلي بالكامل.
الإجابة رقم 1
تغييراتي:
كانت المعلمات العامة T و U زائدة عن الحاجة. كل ما نحتاجه هو T.
ملاحظة: في البداية استبدلت جميع إشاراتك
Uبـkeyof T، ولكن بعد ذلك قمت بسحبها إلىsortArgلتسهيل #2.استخدم أنواع القوالب النصية التي تم تقديمها في TS 4.1
لقد نسيت قص البادئة
-في حالة startsWith('−')استخدم التأكيدات النوعية حيث لا يستطيع TypeScript تضييق النوع إلى ما يجب أن يكون عليه بناءً على سير المنطق (فريق TS يعمل دائمًا على تحسين تحليل سير العمل لمترجم TS، لذا أراهن أنه في يوم من الأيام سيكون هذا تلقائيًا)
تحسينات واجهة برمجة التطبيقات: تم إعادة تسمية الدالة وإضافة دالة
sortملائمة تسهل قراءتها في الكود (انظر مثال الاستخدام الذي يلي كود الحل).
type sortArg<T> = keyof T | `-${string & keyof T}`
/**
* Returns a comparator for objects of type T that can be used by sort
* functions, were T objects are compared by the specified T properties.
*
* @param sortBy - the names of the properties to sort by, in precedence order.
* Prefix any name with `-` to sort it in descending order.
*/
export function byPropertiesOf<T extends object> (sortBy: Array<sortArg<T>>) {
function compareByProperty (arg: sortArg<T>) {
let key: keyof T
let sortOrder = 1
if (typeof arg === 'string' && arg.startsWith('-')) {
sortOrder = -1
// Typescript is not yet smart enough to infer that substring is keyof T
key = arg.substr(1) as keyof T
} else {
// Likewise it is not yet smart enough to infer that arg here is keyof T
key = arg as keyof T
}
return function (a: T, b: T) {
const result = a[key] < b[key] ? -1 : a[key] > b[key] ? 1 : 0
return result * sortOrder
}
}
return function (obj1: T, obj2: T) {
let i = 0
let result = 0
const numberOfProperties = sortBy?.length
while (result === 0 && i < numberOfProperties) {
result = compareByProperty(sortBy[i])(obj1, obj2)
i++
}
return result
}
}
/**
* Sorts an array of T by the specified properties of T.
*
* @param arr - the array to be sorted, all of the same type T
* @param sortBy - the names of the properties to sort by, in precedence order.
* Prefix any name with `-` to sort it in descending order.
*/
export function sort<T extends object> (arr: T[], ...sortBy: Array<sortArg<T>>) {
arr.sort(byPropertiesOf<T>(sortBy))
}
مثال على الاستخدام:
interface User {
name: string
id: string
age?: number
}
const users: User[] = [
{name: 'Harriet Tubman', id: '01', age: 53},
{name: 'John Brown', id: '02', age: 31},
{name: 'John Brown', id: '03', age: 59},
{name: 'James Baldwin', id: '04', age: 42},
{name: 'Greta Thunberg', id: '05', age: 17}
]
// using Array.sort directly
users.sort(byPropertiesOf<User>(['name', '-age', 'id']))
// using the convenience function for much more readable code
sort(users, 'name', '-age', 'id')
