简体   繁体   中英

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

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

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"

  • error is thrown properly at level 1 (as expected)
  • but no error is thrown from level 2 and above. (but expected to throw error)

How should I create types for the above?

Assuming you are using TS3.4 or below:

This is the result of a known issue in which excess property checks are not performed on nested intersection types like 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. 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 .

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:

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

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!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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