【问题标题】:Typescript interface set values based on logic基于逻辑的 Typescript 接口设置值
【发布时间】:2021-10-27 14:43:01
【问题描述】:

我有一个 typescript 接口,定义为:

interface FutureProjection{
  month1: number;
  month2: number;
  .
  .
  .
  .
  .
  .
  month12: number;
}

此数据用于在 UI 上显示网格。 我有一个逻辑,用于找出设置数据的月份:

calculateMonthNum(currentDate:String,futureDate: String):number{
 //Calculate the month difference between future and current.
return monthDiff;
}

functionToSetMonthData(currentDate:String,futureDate: String, projection: FutureProjection, amount: number): FutureProjection{
  const monthNum = calculateDiff(currentDate,futureDate);
  ****//How to do this?? vvvv **
  projection.month+monthNum = amount; //.  <---This assignment operation???
  return projection;
}

【问题讨论】:

    标签: javascript typescript ecmascript-6


    【解决方案1】:

    您可以通过以下方式实现:

      projection[`month${monthNum}`] = amount;
    

    【讨论】:

      【解决方案2】:

      您始终可以使用括号语法来访问任何 javascript 对象的属性。

      // identical
      projection['month1']
      projection.month1
      

      这意味着您可以构建一个作为属性名称的字符串并在括号中使用它:

      projection[`month${monthNum}`] = amount;
      

      但在打字稿中只能让你走到一半。这会给你一个类型错误:

      type 'string' can't be used to index type 'FutureProjection'.
      

      问题是FutureProjection不能被string索引,只允许特定的字符串。所以打字稿不知道month${monthNum} 是一个实际上有效的特定字符串,因为monthNum 可以是任何数字(-5、0、100、3.14159 等)。

      解决这个问题的最简单方法是将密钥字符串转换为keyof FutureProjection。请注意,为了安全起见,您必须确保 calculateDiff() 只返回整数 1 到 12。

      看起来像:

      function setMonthData(currentDate:String,futureDate: String, projection: FutureProjection, amount: number): FutureProjection{
        const monthNum = calculateDiff(currentDate, futureDate);
        const monthKey = `month${monthNum}` as keyof FutureProjection
        projection[monthKey] = amount;
        return projection;
      }
      

      Working example on typescript playground


      话虽如此,使用数组可能会让您的生活更轻松。

      【讨论】:

        【解决方案3】:

        仅供参考,作为string index signature 之类的替代品

        interface FutureProjection {
          [k: string]: number
        }
        

        它可以接受任何密钥:

        declare const f: FutureProjection
        f.oopsiedaisy = 123; // no compiler error
        

        人们可能会考虑使用 TypeScript 4.4 中引入的 template string pattern index signature 来仅接受以 "month" 开头并后跟类似数字的字符串:

        interface FutureProjection {
          [k: `month${number}`]: number
        }
        
        declare const f: FutureProjection
        f.oopsiedaisy = 123; // error
        f.month11 = 456; // okay
        f.month1234567 = 789; // this is also okay, any number is accepted
        

        如果您这样做,编译器将自动允许您使用适当构造的 template literal string 索引到 FutureProjection

        function functionToSetMonthData(currentDate: string, futureDate: string,
          projection: FutureProjection, amount: number
        ): FutureProjection {
          const monthNum = calculateMonthNum(currentDate, futureDate);
          projection[`month${monthNum}`] = amount; // okay
          return projection;
        }
        

        请注意,这并不能完全满足仅接受 month1month12 的目的。您可以决定将number 替换为数字literal typesunion

        type MonthNumber = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
        interface FutureProjection extends Record<`month${MonthNumber}`, number> { }
        
        declare const f: FutureProjection
        f.oopsiedaisy = 123; // error
        f.month11 = 456; // okay
        f.month1234567 = 789; // error
        

        这很棒;不幸的是,您很难让编译器相信您产生number 的任何数学运算实际上都在产生MonthNumber,因为它并没有真正在类型级别进行数学运算(请参阅microsoft/TypeScript#15645):

        declare const i: MonthNumber;
        const j: MonthNumber = 13 - i; // error, even though this must be safe
        const k: MonthNumber = ((i + 6) % 12) || 12; // error, ditto
        

        所以你会发现自己无论如何都在做不安全的断言以及其他一些解决方法:

        function calculateMonthNum(currentDate: string, futureDate: string): MonthNumber {
          return (((((new Date(futureDate).getMonth() - new Date(currentDate).getMonth())
            % 12) + 12) % 12) || 12) as MonthNumber // <-- assert here
        }
        
        function functionToSetMonthData(currentDate: string, futureDate: string,
          projection: FutureProjection, amount: number
        ): FutureProjection {
          const monthNum = calculateMonthNum(currentDate, futureDate);
          projection[`month${monthNum}` as const] = amount; // <-- const assert here
          return projection;
        }
        
        const f = {} as FutureProjection; // assert here
        for (let i = 1; i <= 12; i++) f[`month${i as MonthNumber}`] = 0; // assert here
        

        所以我觉得你最好用`month${number}`

        Playground link to code

        【讨论】:

          【解决方案4】:

          我愿意这样做......

          projection[`month${monthNum }`] = amount;
          

          【讨论】:

            【解决方案5】:

            尝试定义类似的接口

            interface FutureProjection {
              [month: string]: number
            }
            

            然后使用模板字符串将值设置到对象中

            projection[`month${monthNum}`] = amount;
            

            【讨论】:

              猜你喜欢
              • 2016-02-03
              • 2016-12-30
              • 1970-01-01
              • 1970-01-01
              • 2021-12-03
              • 2016-05-21
              • 1970-01-01
              • 1970-01-01
              • 2016-09-27
              相关资源
              最近更新 更多