Skip to content

Commit

Permalink
wip refining ts types for styles
Browse files Browse the repository at this point in the history
  • Loading branch information
kof committed Sep 13, 2021
1 parent 87387ae commit 04e94fb
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 119 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
"mocha": "^3.2.0",
"npmlog": "^4.1.2",
"pre-commit": "^1.1.3",
"prettier": "^1.13.5",
"prettier": "^2.4.0",
"raf": "^3.4.0",
"react": "^16.8.6",
"react-test-renderer": "^16.8.6",
Expand Down
59 changes: 30 additions & 29 deletions packages/jss/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,45 @@ import {Properties as CSSProperties, PropertiesHyphen as CSSPropertiesHyphen} fr
//
// 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> & 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>>>

export type Classes<Name extends string | number | symbol = string> = Record<Name, string>
export type Keyframes<Name extends string = string> = Record<Name, string>

Expand Down
28 changes: 10 additions & 18 deletions packages/jss/tests/types/Styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const styles: Styles<string, Props> = {
textAlign: 'center',
display: 'flex',
width: '100%',
justifyContent: props => (props.flag ? 'center' : undefined)
justifyContent: (props) => (props.flag ? 'center' : undefined)
},
inner: {
textAlign: 'center',
Expand All @@ -37,7 +37,7 @@ const styles: Styles<string, Props> = {
fontSize: 12
}
},
func: props => ({
func: (props) => ({
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
Expand All @@ -47,8 +47,8 @@ const styles: Styles<string, Props> = {
position: 'relative',
pointerEvents: props.flag ? 'none' : null
}),
funcNull: props => null,
funcWithTerm: props => ({
funcNull: (props) => null,
funcWithTerm: (props) => ({
width: props.flag ? 377 : 272,
height: props.flag ? 330 : 400,
boxShadow: '0px 2px 20px rgba(0, 0, 0, 0.08)',
Expand All @@ -68,27 +68,19 @@ const styles: Styles<string, Props> = {
},
'@keyframes fadeIn': {
from: {opacity: 0},
to: {opacity: 1}
to: {opacity: 1},
'50%': {opacity: 0.5}
}
}

// Test supplied Props and Theme
// Verify that nested parameter declarations are banned
const stylesPropsAndTheme: Styles<string, Props, Theme> = {
rootParamDeclaration: ({flag, theme}) => ({
// @ts-expect-error Did you mean to call this expression?
rootParamDeclaration: ({flag, theme}: Props & {theme: Theme}) => ({
fontWeight: 'bold',
// @ts-expect-error
nothingAllowed: ({flag, theme}) => ''
}),
anotherClass: {
color: 'red',
innerParamDeclaration1: ({flag, theme}) => '',
innerParamDeclaration2: ({flag, theme}) => ({
backgroundColor: 'blue',
// @ts-expect-error
nothingAllowed: ({flag, theme}) => ''
})
}
color: ({flag, theme}: Props & {theme: Theme}) => 'red'
})
}

// Test the className types
Expand Down
15 changes: 6 additions & 9 deletions packages/react-jss/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,8 @@ interface WithStylesProps<
/**
* @deprecated Please use `WithStylesProps` instead
*/
type WithStyles<
S extends Styles<any, any, any> | ((theme: any) => Styles<any, any, undefined>)
> = WithStylesProps<S>
type WithStyles<S extends Styles<any, any, any> | ((theme: any) => Styles<any, any, undefined>)> =
WithStylesProps<S>

declare global {
namespace Jss {
Expand All @@ -76,10 +75,10 @@ interface CreateUseStylesOptions<Theme = DefaultTheme> extends BaseOptions<Theme
name?: string
}

declare function createUseStyles<C extends string = string, Props = unknown, Theme = DefaultTheme>(
styles: Styles<C, Props, Theme> | ((theme: Theme) => Styles<C, Props, undefined>),
declare function createUseStyles<C extends string = string, Data = unknown, Theme = DefaultTheme>(
styles: Styles<C, Data, Theme> | ((theme: Theme) => Styles<C, Data, Theme>),
options?: CreateUseStylesOptions<Theme>
): (data?: Props & {theme?: Theme}) => Classes<C>
): (data?: Data & {theme?: Theme}) => Classes<C>

type GetProps<C> = C extends ComponentType<infer P> ? P : never

Expand All @@ -88,9 +87,7 @@ declare function withStyles<ClassNames extends string | number | symbol, Props,
| Styles<ClassNames, Props, Theme>
| ((theme: Theme) => Styles<ClassNames, Props, undefined>),
options?: WithStylesOptions
): <C>(
comp: C
) => ComponentType<
): <C>(comp: C) => ComponentType<
JSX.LibraryManagedAttributes<
C,
Omit<GetProps<C>, 'classes'> & {
Expand Down
66 changes: 13 additions & 53 deletions packages/react-jss/tests/types/createUseStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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'
})
Expand All @@ -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
Expand All @@ -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
})
Expand Down Expand Up @@ -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>({
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)
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) => ({
Expand Down
6 changes: 3 additions & 3 deletions packages/react-jss/tests/types/docs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ type RuleNames = 'myButton' | 'myLabel'
interface ButtonProps {
children?: React.ReactNode
spacing?: number
fontWeight?: string
labelColor?: string
fontWeight?: 'bold'
labelColor?: 'red'
}

interface CustomTheme {
background: string
background: 'gray'
}

const useStyles = createUseStyles<RuleNames, ButtonProps, CustomTheme>({
Expand Down
4 changes: 2 additions & 2 deletions packages/react-jss/tests/types/withStyles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function functionPlainObject(theme: MyTheme) {
anotherClassName: {
fontWeight: 'bold'
}
}
} as const
}
ResultingComponent = withStyles(functionPlainObject)(SimpleComponent)
ComponentTest = () => <ResultingComponent property="" />
Expand Down Expand Up @@ -80,7 +80,7 @@ const plainObject = {
anotherClassName: {
fontWeight: 'bold'
}
}
} as const
ResultingComponent = withStyles(plainObject)(SimpleComponent)
ComponentTest = () => <ResultingComponent property="" />

Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8140,10 +8140,10 @@ preserve@^0.2.0:
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=

prettier@^1.13.5:
version "1.14.2"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.14.2.tgz#0ac1c6e1a90baa22a62925f41963c841983282f9"
integrity sha512-McHPg0n1pIke+A/4VcaS2en+pTNjy4xF+Uuq86u/5dyDO59/TtFZtQ708QIRkEZ3qwKz3GVkVa6mpxK/CpB8Rg==
prettier@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.4.0.tgz#85bdfe0f70c3e777cf13a4ffff39713ca6f64cba"
integrity sha512-DsEPLY1dE5HF3BxCRBmD4uYZ+5DCbvatnolqTqcxEgKVZnL2kUfyu7b8pPQ5+hTBkdhU9SLUmK0/pHb07RE4WQ==

pretty-format@^26.6.2:
version "26.6.2"
Expand Down

0 comments on commit 04e94fb

Please sign in to comment.