Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
WIP: theme() utility function code completion
This will give you code completion in the `theme()` function. The reason
it is still a WIP is that this only works with the default config right
now and not 100% sure if it is possible to define generics in JSDoc.

The idea would be to:
- Provide types from the default config
- Provide types from the custom config (e.g.: 3rd party plugin)
- Override default config types with the overrides of the user's config

Right now this only provides types for the defaultConfig which might
result in dropping all of this in favor of a much simpler:

```ts
theme<D = any>(path: string, defaultValue: D) => D
```

But this sadly doesn't give you "nice" auto completion. However, the
default might be good enough if we don't error on for example
`theme('customPlugin')` which is currently not the case.
  • Loading branch information
RobinMalfait committed Mar 31, 2022
commit ad98f8b3c3a1ce9675eb42acf8e7be021a8be5b6
46 changes: 45 additions & 1 deletion types/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,53 @@ interface RecursiveKeyValuePair<K extends keyof any = string, V = string> {
}
type ResolvableTo<T> = T | ((utils: PluginUtils) => T)

type DotNotation<T, K extends keyof T = keyof T> = K extends string
? T[K] extends Record<string, any>
? T[K] extends any[]
? K | `${K}.${DotNotation<T[K], Exclude<keyof T[K], keyof any[]>>}`
: K | `${K}.${DotNotation<T[K], keyof T[K]>}`
: K
: never
type Path<T> = DotNotation<T> | keyof T
type PathValue<T, P extends Path<T>> = P extends `${infer K}.${infer Rest}`
? K extends keyof T
? Rest extends Path<T[K]>
? PathValue<T[K], Rest>
: never
: never
: P extends keyof T
? T[P]
: never

// Removes arbitrary values like: { [key: string]: any }
type WithoutStringKey<T> = { [K in keyof T as string extends K ? never : K]: T[K] }

type Unpack<T> = T extends ResolvableTo<infer R>
? { [K in keyof R]: Unpack<R[K]> }
: T extends object
? { [K in keyof T]: Unpack<T[K]> }
: T

// This will remove all the callbacks and simplify it to the actual object
// it uses or the object it returns. It will also remove the `extend`
// section because at runtime that's gone anyway.
type UnpackedTheme = Omit<WithoutStringKey<Unpack<Config['theme']>>, 'extend'>

// This will add additional information purely for code completion. E.g.:
type AugmentedTheme = Expand<Omit<UnpackedTheme, 'colors'> & { colors: DefaultColors }>

interface PluginUtils {
colors: DefaultColors
theme(path: string, defaultValue: unknown): keyof ThemeConfig

// Dynamic based on (default) theme config
theme<P extends Path<AugmentedTheme>, TDefaultValue>(
path: P,
defaultValue?: TDefaultValue
): PathValue<AugmentedTheme, P>
// Path is just a string, useful for third party plugins where we don't
// know the resulting type without generics.
theme<TDefaultValue = any>(path: string, defaultValue?: TDefaultValue): TDefaultValue

breakpoints<I = Record<string, unknown>, O = I>(arg: I): O
rgb(arg: string): (arg: Partial<{ opacityVariable: string; opacityValue: number }>) => string
hsl(arg: string): (arg: Partial<{ opacityVariable: string; opacityValue: number }>) => string
Expand Down