【问题标题】:Typescript array-like type with same generic type between keys of each itemTypescript 类数组类型,在每个项目的键之间具有相同的泛型类型
【发布时间】:2023-03-26 18:37:01
【问题描述】:

我想写一个打字稿函数,接受这样的参数:

myFunc([
  {
    initialValue: 6, // number
    finalValue: 8    // number
  },
  {
    initialValue: 'hello', // string
    finalValue: 'goodbye'  // string
  }
])

但是如果给出这个会失败:

myFunc([
  {
    initialValue: 6, // number
    finalValue: 'goodbye' // should fail because not a number!
  }
])

感觉解决方案应该需要泛型,但泛型是每个数组项中的泛型,而不是整个数组中的泛型。

编辑:我想要一个适用于任何类型值的解决方案,而不仅仅是字符串或整数。我可能也需要将它用于类/函数。

【问题讨论】:

    标签: typescript


    【解决方案1】:

    也许您可以尝试以下方法。我不能 100% 确定它是否正确,但它适用于您上面提到的场景。

    interface type1 {
        arg1: Array<string>,
        arg2: Array<string>
    }
    
    interface type2 {
        arg1: Array<number>,
        arg2: Array<number>
    }
    
    var t = function Test(arg: type1 | type2) {
        alert(arg.arg1);
        alert(arg.arg2);
    }
    
    var arg = { arg1: [2], arg2: [1]};
    t(arg);
    

    让我知道你的想法。

    【讨论】:

      【解决方案2】:

      您可以使用泛型和映射类型来实现类似的工作。我们将使用类型参数来捕获参数的实际类型(无论是什么,即使它确实包含无效的初始/最终对)。然后我们将此类型转换为一个新类型,其中finalValue 是根据传入的实际initialValue 键入的。我们在参数类型中使用这个新类型与一个交集。这意味着参数的类型被推断到类型参数中,但检查了转换后的类型:

      type FinalValues<T extends Array<{ initialValue: any }>> = {
          [P in keyof T]: T[P] extends { initialValue: infer I } ? { initialValue: I, finalValue: I }: never 
      }
      
      function myFunc<T extends [{ initialValue: any }] | Array<{ initialValue: any }>>(v: T & FinalValues<T>): FinalValues<T> {
      
      }
      
      myFunc([
        {
          initialValue: 6, // number
          finalValue: 8    // number
        },
        {
          initialValue: 'hello', // string
          finalValue: 'goodbye'  // string
        }
      ])
      
      myFunc([
        {
          initialValue: 6, // number
          finalValue: "string"    // err
        },
        {
          initialValue: 'hello', // string
          finalValue: 1  // err
        }
      ])
      

      Play

      您还可以更有创意并添加一种自定义错误,以使错误更具可读性:Play

      【讨论】:

      • 这是我认为最好的答案
      • 同意,这是最好的答案,因为它可以处理任何类型,而不仅仅是字符串或数字
      【解决方案3】:

      使用泛型你可以这样实现:

      const myFn = <T>(args: { [p: string]: T }[]) => {
      
      }
      
      myFn<string>([
          {someArg: 'foo', someOtherArg: 'bar'}
      ])
      
      myFn<number>([
          {
              someArg: 5,
              someOtherArg: 9,
          }
      ])
      
      // ERROR
      myFn<string>([
          {
              someArg: 'asdf',
              someOtherArg: 5,
          }
      ])
      

      如果你知道你的对象应该只有特定的参数,你可以用更具体的类型来做同样的事情

      const myFn = <T>(args: { initialValue: T, finalValue: T }[]) => {
      
      }
      

      【讨论】:

      • 在阅读问题时,请求是将initialValuefinalValue 关联起来,它们在项目之间可能不同,但它们必须与项目相同。如果项目之间的类型不同,这将不起作用
      • 是的,我注意到在第二次阅读时,这就是我赞成你的答案的原因。
      • 哎呀,没注意到是你,我的错?
      【解决方案4】:

      实现此类目标的另一种方法是使用类。

      缺点是它不能完全复制你想要的东西,但它非常接近。好处是非常简单易懂。

      class Arg<T> {
          initialValue: T
          finalValue: T
      
          constructor({ initialValue, finalValue }: { initialValue: T, finalValue: T }) {
              this.initialValue = initialValue
              this.finalValue = finalValue
          }
      }
      
      function myFunc(input: Arg<unknown>[]) {
      
      }
      
      myFunc([
          new Arg({
              initialValue: 6, // number
              finalValue: 8    // number
          }),
          new Arg({
              initialValue: 'hello', // string
              finalValue: 'goodbye'    // string
          }),
      ])
      
      myFunc([
        new Arg({
          initialValue: 6, // number
          finalValue: 'string' // ERROR
        }),
      ])
      

      【讨论】:

        【解决方案5】:

        解决方案是将列表元素类型定义为求和类型,其中成员或两个字符串或两个数字。

        type A = {
          initialValue: number
          finalValue: number
        }
        type B = {
          initialValue: string
          finalValue: string
        }
        
        type ListAB = (A | B) [] // type contains or A(two numbers) or B(two strings)
        
        

        现在ListAB 类型的数组严格按照 Sum A | B 进行类型化。示例用法:

        
        function myFunc(list: ListAB) {
          // implementation
        }
        
        // correct
        myFunc([
          {
            initialValue: 6, // number
            finalValue: 8    // number
          },
          {
            initialValue: 'hello', // string
            finalValue: 'goodbye'  // string
          }
        ])
        
        // will have error
        myFunc([
          {
            initialValue: 6, // number
            finalValue: 'a'    // string
          },
          {
            initialValue: 1, // number
            finalValue: 'goodbye'  // string
          }
        ])
        

        【讨论】:

        • 我同意您的解决方案,因为它更简单、更直接,可供其他开发人员阅读和理解。
        【解决方案6】:

        我赞成 Maciej 的帖子,因为它已经完成,您可能想要开始定义您的类型。

        只是为了提供一个快速而肮脏的解决方案(这对我来说绝对是一种方式,更简单的解决方案,直截了当)。不过我会做像 Maciej 这样的类型......

        public myFunc(arr: ({ initialValue: string, finalValue: string } | { initialValue: number, finalValue: number })[])
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-01-04
          • 1970-01-01
          • 1970-01-01
          • 2023-02-09
          • 2019-05-30
          • 1970-01-01
          • 1970-01-01
          • 2021-06-11
          相关资源
          最近更新 更多