简体   繁体   English

如何在CSS样式的打字稿中创建递归对象类型?

[英]How do I create recursive object type in typescript for css styles?

I am using React.CSSProperties for creating css variables so I can create like 我正在使用React.CSSProperties创建CSS变量,所以我可以创建像

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

I am trying to create a theme which will have css variables at the root level or nested inside modifiers like size(sm,md,lg) or variant(light,dark) or other as below 我正在尝试创建一个主题,该主题将在根级别具有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"
            }
          }
        }
      }
    }
  }
}

Basically I am looking for recursive object type in typescript like below, but ending up in circular reference? 基本上,我正在寻找如下的打字稿中的递归对象类型,但最终以循环引用结束?

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

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

I am able to find the answer (below) close to it. 我能够找到接近它的答案。

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

But facing some issue. 但是面临一些问题。 If modifier name is given invalid like "btn" instead of "button" 如果给定修饰符名称,则像“ btn”而不是“ button”一样无效

  • error is thrown properly at level 1 (as expected) 在第1级上正确抛出了错误(按预期)
  • but no error is thrown from level 2 and above. 但是从2级及更高级别不会引发任何错误。 (but expected to throw error) (但预计会引发错误)

How should I create types for the above? 我应该如何为上述类型创建类型?

Assuming you are using TS3.4 or below: 假设您使用的是TS3.4或更低版本:

This is the result of a known issue in which excess property checks are not performed on nested intersection types like Theme . 这是一个已知问题的结果,在该问题中,未对嵌套交集类型(例如Theme执行过多的属性检查 Technically speaking, types in TypeScript are generally "open" or "extendable", meaning it's okay for a value of a type to have more properties than mentioned in its type definition. 从技术上讲,TypeScript中的类型通常是“开放的”或“可扩展的”,这意味着类型的值具有比其类型定义中提及的更多的属性是可以的。 You rely on this with things like interface A { a: string } and interface B extends A { b: string } , where a value of type B is also a value of type A . 您可以通过interface A { a: string }interface B extends A { b: string }依赖它,其中类型B的值也是类型A的值。

But usually when people are using object literals like you are, it's a mistake to add extra properties... especially in your case where the "extra" property is just a typographical error for an optional property. 但是通常当人们像您一样使用对象文字时,添加额外的属性是一个错误……尤其是在您的情况下,“额外”属性只是可选属性的印刷错误。 So the compiler decides that when you're assigning a new object literal to a type, extra properties are an error. 因此,编译器决定,当您为类型分配新的对象文字时,额外的属性会出错。 That is, types of fresh object literals are "closed" or " exact "... 也就是说,新鲜对象文字的类型是“封闭”或“ 精确 ” ...

...well, or they should be, except for this bug with nested intersections. ...嗯,或者应该这样,除了这个带有嵌套相交的错误。 In TypeScript 3.4 or below, this is the behavior: 在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 游乐场链接

Luckily for you, this error has been fixed for TypeScript 3.5, which should be released on May 30, 2019, or you could try out the nightly build of TypeScript via typescript@next 幸运的是,此错误已针对TypeScript 3.5 修复 ,该错误应 2019年5月30日发布 ,或者您可以通过typescript@next尝试每晚构建TypeScript

If you can't wait until then or can't upgrade soon, you can work around this by using a mapped type to replace the intersection with a more normal-looking object: 如果您不能等到那时或不能很快升级,可以通过使用映射类型将交集替换为外观更普通的对象来解决此问题:

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 游乐场链接

The expected error shows up, and all is right with the world. 出现预期的错误,世界一切正常。 Hope that helps; 希望能有所帮助; good luck! 祝好运!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM