代码之家  ›  专栏  ›  技术社区  ›  Ben Carp

Typescript-我可以定义混合对象的类型而不分别检查每个属性吗?

  •  1
  • Ben Carp  · 技术社区  · 7 年前

    export const COLORS = {
       GREEN_BACKGROUND: "#00d8ae",
       GREEN_TEXT: "#038c72",
       BLUE: "#162044",
       GRAYS: Array.from(Array(15).keys()).map(
          (_, i) => {
             const brightness = 16 * (1 + i)
             return `rgb(${brightness}, ${brightness}, ${brightness})`
           },
       ),
    }
    

    我在找一个定义这种对象类型的速记。如果没有格雷斯阵列,我们可以:

    interface IColors = {
        [k:string] : string
    }
    

    但正在努力

    interface IColors {
        [k:string] : string
        Grays: string[]   
    }
    

    因为Grays对象不遵循上面一行中设置的规则,所以产生一个错误。

    目前是否有一种方法可以在不单独定义每个属性的类型的情况下编写这样的类型? 我们当然可以使用联合类型

    interface IColors {
        [k:string] : string | string[]
    }
    

    但这会扩大个别颜色的种类,阻止我们通过颜色。蓝色例如字符串。

    1 回复  |  直到 7 年前
        1
  •  1
  •   jcalz    7 年前

    我相信这个问题已经在其他地方得到了回答,但我的搜索能力还不够强大。目前还没有完美的解决方案。以下是可能的方法:

    • 带有联合类型的索引签名简单且类型安全,但正如您所指出的,它比您试图描述的类型要宽。

    • intersection 键入如下:

      type IColors = {[k: string]: string} & {GRAYS: string[]};
      declare const COLORS: IColors;
      const str = COLORS.PURPLE; // string
      const arr = COLORS.GRAYS; // string[]
      

      当你阅读 COLORS

      // Property 'GRAYS' is incompatible with index signature.
      const COLORS: IColors = { ... }
      

      你必须用类似的方法 Object.assign() :

      // okay
      const COLORS: IColors = Object.assign({
        GREEN_BACKGROUND: "#00d8ae",
        GREEN_TEXT: "#038c72",
        BLUE: "#162044"
      }, {
          GRAYS: Array.from(Array(15).keys()).map(
            (_, i) => {
              const brightness = 16 * (1 + i)
              return `rgb(${brightness}, ${brightness}, ${brightness})`
            },
          ),
      });
      
    • 您可以放弃索引签名并使用 generic , conditional 类型来表示“一堆 string "GRAYS" string[] 财产“。。。以及一个helper函数来推断常量的泛型类型 :

      type IColors<K extends keyof any> = { [P in K]: P extends "GRAYS" ? string[] : string }
      const asIColors = <T extends IColors<keyof T>>(
        iColors: T) => iColors;
      
      const COLORS = asIColors({
        GREEN_BACKGROUND: "#00d8ae",
        GREEN_TEXT: "#038c72",
        BLUE: "#162044",
        GRAYS: Array.from(Array(15).keys()).map(
          (_, i) => {
            const brightness = 16 * (1 + i)
            return `rgb(${brightness}, ${brightness}, ${brightness})`
          },
        ),
      });
      

    • 您可以完全放弃这种混合类型,并屈从于TypeScript的意愿,而不是相反。你最终可能会更快乐:

      interface IColors {
        individualStrings: { [k: string]: string };
        GRAYS: string[];
      }
      
      const COLORS: IColors = {
        individualStrings: {
          GREEN_BACKGROUND: "#00d8ae",
          GREEN_TEXT: "#038c72",
          BLUE: "#162044"
        },
        GRAYS: Array.from(Array(15).keys()).map(
          (_, i) => {
            const brightness = 16 * (1 + i)
            return `rgb(${brightness}, ${brightness}, ${brightness})`
          },
        ),
      };
      

      是的,它的结构不一样,但它足够简单,类型安全,而且易于使用。

    其中哪一个,如果有的话,你想使用取决于你和你的用例。希望有帮助;祝你好运!

    推荐文章