【问题标题】:How do I create recursive object type in typescript for css styles?如何在 typescript 中为 css 样式创建递归对象类型?
【发布时间】:2019-09-27 11:37:04
【问题描述】:

我正在使用 React.CSSProperties 创建 css 变量,所以我可以像这样创建

let css: React.CSSProperties = {
  position: "relative",
  display: "inline-block",
  background: "red"
}

我正在尝试创建一个主题,该主题将在根级别具有 css 变量或嵌套在修饰符中,例如 size(sm,md,lg) 或 variant(light,dark) 或其他如下

let theme: MyTheme = {
  background: "red",       // css
  color: "white",          // css  

  button: {                // custom: component name Level1
    background: "red",     // css
    color: "white",        // css

    light: {               // custom: modifier variant Level2
      background: "red",   // css
      color: "white",      // css

      hollow: {            // custom: modifier Level3
        borderColor: "red",
        color: "red",
        background: "transparent",
      }
    }
    dark: {
      background: "red",
      color: "white",

      hollow: {
        borderColor: "black",
        color: "black",
        background: "transparent",

        modfierLevel4: {
          some: "css"

          modfierLevel5: {
            some: "css"

            modfierLevel6: {
              some: "css"
            }
          }
        }
      }
    }
  }
}

基本上我正在寻找如下打字稿中的递归对象类型,但最终以循环引用结束?

type Modifiers = 'button' | 'dark' | 'light' | 'otherModifiers'

interface Theme extends React.CSSProperties {
 [k in Modifiers]?: Theme;
}

我能够找到答案(如下)接近它。

type Theme = React.CSSProperties & { [keys in Modifiers]?: Theme }

但面临一些问题。如果给定的修饰符名称无效,例如“btn”而不是“button”

  • 错误在 1 级正确抛出(如预期)
  • 但是从级别 2 及以上不会引发错误。 (但预计会抛出错误)

我应该如何为上述创建类型?

【问题讨论】:

    标签: css reactjs typescript recursion


    【解决方案1】:

    假设您使用的是 TS3.4 或以下版本:

    这是known issue 的结果,其中excess property checks 未对Theme 等嵌套交集类型执行。从技术上讲,TypeScript 中的类型通常是“开放的”或“可扩展的”,这意味着一个类型的值可以拥有比其类型定义中提到的更多的属性。您可以使用interface A { a: string }interface B extends A { b: string } 之类的东西来依赖它,其中B 类型的值也是A 类型的值。

    但通常当人们像您一样使用对象文字时,添加额外属性是错误的......尤其是在您的情况下,“额外”属性只是可选属性的印刷错误。所以编译器决定当你将一个新的对象字面量分配给一个类型时,额外的属性是一个错误。也就是说,新鲜对象字面量的类型是“关闭”或“exact”...

    ...嗯,或者他们应该是,除了这个带有嵌套交叉点的错误。在 TypeScript 3.4 或更低版本中,这是行为:

    type Foo = { baseProp: string, recursiveProp?: Foo };
    const foo: Foo = {
        baseProp: "a",
        recursiveProp: {
            baseProp: "b",
            extraProp: "error as expected" // error ?
        }
    }
    
    type Bar = { baseProp: string } & { recursiveProp?: Bar };
    const bar: Bar = {
        baseProp: "a",
        recursiveProp: {
            baseProp: "b",
            extraProp: "no error?!" // no error in TS3.4 ?
        }
    }
    

    Playground link

    幸运的是,TypeScript 3.5 的此错误是 fixed,它应该是 2019 年 5 月 30 日的 released,或者您可以通过 typescript@next 试用 TypeScript 的夜间版本

    如果您不能等到那个时候或不能很快升级,您可以通过使用映射类型将交集替换为看起来更正常的对象来解决此问题:

    type Id<T> = { [K in keyof T]: T[K] };
    type Bar = Id<{ baseProp: string } & { recursiveProp?: Bar }>;
    const bar: Bar = {
        baseProp: "a",
        recursiveProp: {
            baseProp: "b",
            recursiveProp: {
                baseProp: "c",
                extraProp: "error yay" ?
            }
        }
    }
    

    Playground link

    出现了预期的错误,一切正常。希望有帮助;祝你好运!

    【讨论】:

    • 如果“修饰符”类型定义明确,这将正常工作。但是,如果创建像type Modifiers = 'dark' | 'light' | string;,那么我将面临错误。有什么方法可以为 any 自定义键创建嵌套类型?
    • type Modifiers = 'dark' | 'light' | stringtype Modifiers = string 相同。因此,此时您只是使用索引签名,并且需要确保值类型符合React.CSSProperties 中的值类型。这似乎是一个单独的问题,需要自己的代码和特定的错误等,以便有人回答。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 2020-03-11
    • 2012-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多