-
-
Notifications
You must be signed in to change notification settings - Fork 399
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refine style types #1554
base: master
Are you sure you want to change the base?
Refine style types #1554
Changes from all commits
a570f35
f55414e
81ec5b9
d2a2b4b
87387ae
04e94fb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,49 +1,50 @@ | ||
import {Properties as CSSProperties} from 'csstype' | ||
import {Properties as CSSProperties, PropertiesHyphen as CSSPropertiesHyphen} from 'csstype' | ||
|
||
// Observable support is included as a plugin. Including it here allows | ||
// TypeScript users to use Observables, which could be confusing if a user | ||
// hasn't installed that plugin. | ||
// | ||
// TODO: refactor to only include Observable types if plugin is installed. | ||
export interface MinimalObservable<T> { | ||
subscribe( | ||
nextOrObserver: ((value: T) => void) | {next: (value: T) => void} | ||
): {unsubscribe: () => void} | ||
subscribe(nextOrObserver: ((value: T) => void) | {next: (value: T) => void}): { | ||
unsubscribe: () => void | ||
} | ||
} | ||
|
||
type Func<P, T, R> = T extends undefined ? ((data: P) => R) : ((data: P & {theme: T}) => R) | ||
type Func<Data, Theme, Style> = Theme extends undefined | ||
? (data: Data) => Style | null | undefined | ||
: (data: Data & {theme: Theme}) => Style | null | undefined | ||
|
||
type NormalCssProperties = CSSProperties<string | number> | ||
type NormalCssProperties = CSSProperties<string | number> & CSSPropertiesHyphen<string | number> | ||
type NormalCssValues<K> = K extends keyof NormalCssProperties ? NormalCssProperties[K] : JssValue | ||
|
||
export type JssStyle<Props = any, Theme = undefined> = | ||
| { | ||
[K in keyof NormalCssProperties]: | ||
| NormalCssValues<K> | ||
| JssStyle<Props, Theme> | ||
| Func<Props, Theme, NormalCssValues<K> | JssStyle<undefined, undefined> | undefined> | ||
| MinimalObservable<NormalCssValues<K> | JssStyle | undefined> | ||
} | ||
| { | ||
[K: string]: | ||
| JssValue | ||
| JssStyle<Props, Theme> | ||
| Func<Props, Theme, JssValue | JssStyle<undefined, undefined> | undefined> | ||
| MinimalObservable<JssValue | JssStyle | undefined> | ||
} | ||
|
||
export type JssStyle<Data = any, Theme = undefined> = { | ||
[Key in keyof NormalCssProperties]: | ||
| NormalCssValues<Key> | ||
| JssStyle<Data, Theme> | ||
| Func<Data, Theme, NormalCssValues<Key> | undefined> | ||
| MinimalObservable<NormalCssValues<Key> | JssStyle | undefined> | ||
| null | ||
| undefined | ||
} & // nesting | ||
{ | ||
[Key: `${string}&${string}`]: JssStyle<Data, Theme> | null | undefined | ||
} | ||
export type Styles< | ||
Name extends string | number | symbol = string, | ||
Props = unknown, | ||
Data = unknown, | ||
Theme = undefined | ||
> = Record< | ||
Name, | ||
| JssStyle<Props, Theme> | ||
| Array<JssStyle<Props, Theme>> | ||
| string | ||
| Func<Props, Theme, JssStyle<undefined, undefined> | string | null | undefined> | ||
| MinimalObservable<JssStyle | string | null | undefined> | ||
> | ||
> = | ||
| Record< | ||
Name, | ||
| JssStyle<Data, Theme> | ||
| Array<JssStyle<Data, Theme>> | ||
| string | ||
| Func<Data, Theme, JssStyle<undefined, undefined> | string | null | undefined> | ||
| MinimalObservable<JssStyle | string | null | undefined> | ||
> | ||
| Record<`@keyframes ${string}`, Record<'from' | 'to' | `${number}%`, JssStyle<Data, Theme>>> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is need to add |
||
|
||
export type Classes<Name extends string | number | symbol = string> = Record<Name, string> | ||
export type Keyframes<Name extends string = string> = Record<Name, string> | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,7 +16,7 @@ const expectedCustomTheme: MyTheme = {color: 'red'} | |
|
||
/* -------------------- THEME ARGUMENT -------------------- */ | ||
// Regular, static styles work fine | ||
const themeArg1 = createUseStyles(theme => ({ | ||
const themeArg1 = createUseStyles((theme) => ({ | ||
someClassName: '', | ||
anotherClassName: { | ||
fontWeight: 'bold' | ||
|
@@ -26,20 +26,20 @@ const themeArg1ClassesPass = themeArg1() | |
|
||
// Theme type assumed to be the default | ||
// Nested theme declaration banned | ||
// @ts-expect-error | ||
const themeArg2 = createUseStyles(theme => ({ | ||
themeNotAllowed: ({theme: innerTheme}) => ({ | ||
fontWeight: 'bold' | ||
const themeArg2 = createUseStyles((parentTheme) => ({ | ||
themeNotAllowed: ({theme}: {theme: MyTheme}) => ({ | ||
color: theme.color | ||
}) | ||
})) | ||
// @ts-expect-error | ||
const themeArg2ClassesFail = themeArg2({theme: {}}) | ||
// @ts-expect-error | ||
const themeArg2ClassesFail2 = themeArg2({theme: expectedCustomTheme}) | ||
const themeArg2ClassesPass = themeArg2({theme: expectedDefaultTheme}) | ||
// @ts-expect-error | ||
const themeArg2ClassesFail2 = themeArg2({theme: expectedDefaultTheme}) | ||
|
||
// Props declaration is allowed | ||
const themeArg3 = createUseStyles<string, MyProps>(theme => ({ | ||
const themeArg3 = createUseStyles<string, MyProps>((theme) => ({ | ||
onlyPropsAllowed: ({...props}) => ({ | ||
fontWeight: 'bold' | ||
}) | ||
|
@@ -52,16 +52,15 @@ const themeArg3ClassesPass = themeArg3(expectedCustomProps) | |
const themeArg3ClassesPass2 = themeArg3({...expectedCustomProps, theme: expectedDefaultTheme}) | ||
|
||
// Nested props declaration banned | ||
const themeArg4 = createUseStyles<string, MyProps>(theme => ({ | ||
const themeArg4 = createUseStyles<string, MyProps>((theme) => ({ | ||
onlyPropsAllowed: ({...props}) => ({ | ||
fontWeight: 'bold', | ||
// @ts-expect-error | ||
propsNotAllowed: ({...innerProps}) => '' | ||
}) | ||
})) | ||
|
||
// Supplied theme type is acknowledged | ||
const themeArg5 = createUseStyles<string, unknown, MyTheme>(theme => ({})) | ||
const themeArg5 = createUseStyles<string, unknown, MyTheme>((theme) => ({})) | ||
// @ts-expect-error | ||
const themeArg5ClassesFail = themeArg5({theme: {}}) | ||
// @ts-expect-error | ||
|
@@ -81,7 +80,7 @@ const themeArg6ClassesFail2 = themeArg6({theme: expectedDefaultTheme}) | |
const themeArg6ClassesPass = themeArg6({theme: expectedCustomTheme}) | ||
|
||
// Props can be determined implicitly | ||
const themeArg7 = createUseStyles(theme => ({ | ||
const themeArg7 = createUseStyles((theme) => ({ | ||
checkbox: ({property}: MyProps) => ({ | ||
borderColor: property | ||
}) | ||
|
@@ -172,53 +171,14 @@ const noThemeArg4ClassesPass2 = noThemeArg4({...expectedCustomProps, theme: expe | |
const noThemeArg5 = createUseStyles<string, MyProps, MyTheme>({ | ||
singleNest: { | ||
fontWeight: 'bold', | ||
singleValue: ({property, theme}) => '', | ||
nestOne: ({property, theme}) => ({ | ||
// @ts-expect-error | ||
nestOne: ({property, theme}: MyProps & {theme: MyTheme}) => ({ | ||
color: 'red', | ||
// @ts-expect-error | ||
nothingAllowed: ({theme: innerTheme, ...innerProps}) => '' | ||
display: () => 'block' | ||
}) | ||
} | ||
}) | ||
|
||
// Nested declarations are banned (double nest test) | ||
const noThemeArg6 = createUseStyles<string, MyProps, MyTheme>({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't clearly understand what this is testing, because nesting is not generally disallowed, its just only working with nested syntax with & |
||
doubleNest: { | ||
fontWeight: 'bold', | ||
singleValue: ({property, theme}) => '', | ||
firstNest: { | ||
color: 'red', | ||
innerSingleValue: ({property, theme}) => '', | ||
secondNest: ({property, theme}) => ({ | ||
backgroundColor: 'blue', | ||
// @ts-expect-error | ||
nothingAllowed: ({theme: innerTheme, ...innerProps}) => '' | ||
}) | ||
} | ||
} | ||
}) | ||
|
||
// Nested declarations are banned (triple nest test) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why would one need double nest and triple nest test, is there code that is explicitly working in one of them and not in another? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know merging of some commits happened earlier. Not sure if this PR holds any of those. My comments on those kinds of tests are probably misleading. What it should say is probably something like
In terms of function declarations, no, I'm not aware of any existing code that tries to nest function declarations. But I thought that I read on the docs somewhere (or in a console warning) that nested functions were not supported. This test was just to ensure that the types would also error out if someone tried to create a nested function (before the runtime bites them). I did "double" and "triple" failure tests for function declarations just to make sure the type was appropriately defined. This was for two main reasons:
Theoretically speaking, a person could nest as much as they want. But testing every Nth scenario would be impractical. I stopped at 2nd and 3rd level nesting to verify that our recursive types were properly defined. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This probably also answers #1554 (comment) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The confusing part was triple test, if we just forbid double nest its already solves the problem There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay. If you think the double nest is sufficient, I think we can remove all the triple-nest tests. Makes the file shorter 😅 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean if double nesting is already prevented, x nesting is also prevented. It was just confusing to see 3x level of nesting because I start thinking why would you need to test a 3rd level, whats special about it |
||
const noThemeArg7 = createUseStyles<string, MyProps, MyTheme>({ | ||
tripleNest: { | ||
fontWeight: 'bold', | ||
singleValue: ({property, theme}) => '', | ||
firstNest: { | ||
color: 'red', | ||
innerSingleValue: ({property, theme}) => '', | ||
secondNest: { | ||
backgroundColor: 'blue', | ||
innerMostSingleValue: ({property, theme}) => '', | ||
thirdNest: ({property, theme}) => ({ | ||
display: 'block', | ||
// @ts-expect-error | ||
nothingAllowed: ({theme: innerMostTheme, ...innerMostProps}) => '' | ||
}) | ||
} | ||
} | ||
} | ||
}) | ||
|
||
// Props can be determined implicitly | ||
const noThemeArg8 = createUseStyles({ | ||
checkbox: ({property}: MyProps) => ({ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was supposed to be Data all along, the fact it can be Props is up to user, it could be anything