typescript - 如何扩展 keyof 类型以使其包含 key 的修改版本,例如以 '-' 为前缀?

标签 typescript

例如,我想将 Typescript 类型安全添加到 vanilla Javascript Sort array of objects by string property value解决方案。它接受要排序的对象的 args 键,以确定要排序的键。如果 key 以 - 为前缀,排序相反。
我将如何输入 arg 以接受,例如,"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'])

typescript playground
在最后一行我看到错误
Type '"-age"' is not assignable to type 'keyof User'.
有什么方法可以扩展keyof User是否正确,以便以“-”为前缀的值仍被视为该类型的有效值?
即使您完全更换我的解决方案,我也会很感激。

最佳答案

我的变化:

  • T 和 U 通用参数是多余的。只需要T。

    Note: I originally just replaced all your U references with keyof T, but then pulled it out to sortArg to facilitate #2.


  • 使用 Template Literal Types在 TS 4.1 中引入
  • 您忘记修剪 - startsWith('-') 情况下的前缀
  • 在 Typescript 无法缩小类型的地方使用类型断言
    必须给它的逻辑流程(TS 团队一直在改进
    TS 编译器的流程分析,所以我打赌有一天这将是自动的)
  • API 改进:重命名函数并添加一个方便的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 is not 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')
    

    关于typescript - 如何扩展 keyof 类型以使其包含 key 的修改版本,例如以 '-' 为前缀?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68278850/

    相关文章:

    typescript - Visual Studio Code - 缩小和混淆编译的 TypeScript

    Angular - 日期管

    javascript - 人们分配变量后,Angular 6/Typescript 属性为 "undefined"

    angular - IE11 Angular 2 错误 "Expected identifier, string or number"

    javascript - 错误 : Effect dispatched an invalid action: undefined

    javascript - 需要在 ng-template 中解释 let-* 指令

    javascript - 如何在 TypeScript/JavaScript 中缩写驼峰式文本字符串

    angular - 动态添加事件监听器

    typescript - 是否可以实现 Y-combinator 的 TypeScript 通用版本?

    angular - Ngx-datatable 行详细信息