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"
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 😕
}
}
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" 👍
}
}
}
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.