Skip to content

Commit

Permalink
Improve resolveConfig return type: merge themes (#12272)
Browse files Browse the repository at this point in the history
* Generate types: do not intersect with Config theme type when generating DefaultTheme

* Merge default theme in ResolvedConfig

* UnwrapResolvables on theme.extend as well

* Apply extend to overrides and default theme

* Omit extend from DefaultTheme

* Relax generic constraints, better generic variable names

* Fall back to ThemeConfig if key not in DefaultTheme

* Split out ThemeConfigCustomizable to avoid anys in ThemeConfigResolved

* Allow custom theme properties

* handle TypeScript error

* apply prettier formatting

* update changelog

* change type name

---------

Co-authored-by: Nikita Gaidakov <ngaidakov@podfather.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
  • Loading branch information
3 people authored and thecrypticace committed Oct 30, 2023
1 parent ac171f0 commit ffadf2b
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Don’t add spaces to negative numbers following a comma ([#12324](https://github.com/tailwindlabs/tailwindcss/pull/12324))
- Don't emit `@config` in CSS when watching via the CLI ([#12327](https://github.com/tailwindlabs/tailwindcss/pull/12327))
- Improve types for `resolveConfig` ([#12272](https://github.com/tailwindlabs/tailwindcss/pull/12272))

## [3.3.5] - 2023-10-25

Expand Down
25 changes: 22 additions & 3 deletions resolveConfig.d.ts
@@ -1,11 +1,30 @@
import type { Config, ResolvableTo } from './types/config'
import { Config, ResolvableTo, ThemeConfig } from './types/config'
import { DefaultTheme } from './types/generated/default-theme'
import { DefaultColors } from './types/generated/colors'

type ResolvedConfig<T extends Config> = Omit<T, 'theme'> & {
theme: MergeThemes<
UnwrapResolvables<Omit<T['theme'], 'extend'>>,
T['theme'] extends { extend: infer TExtend } ? UnwrapResolvables<TExtend> : {}
>
}

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

type ResolvedConfig<T extends Config> = Omit<T, 'theme'> & {
theme: UnwrapResolvables<T['theme']>
type ThemeConfigResolved = UnwrapResolvables<ThemeConfig>
type DefaultThemeFull = DefaultTheme & { colors: DefaultColors }

type MergeThemes<Overrides extends object, Extensions extends object> = {
[K in keyof ThemeConfigResolved | keyof Overrides]: (K extends keyof Overrides
? Overrides[K]
: K extends keyof DefaultThemeFull
? DefaultThemeFull[K]
: K extends keyof ThemeConfigResolved
? ThemeConfigResolved[K]
: never) &
(K extends keyof Extensions ? Extensions[K] : {})
}

declare function resolveConfig<T extends Config>(config: T): ResolvedConfig<T>
Expand Down
3 changes: 1 addition & 2 deletions scripts/generate-types.js
Expand Up @@ -91,9 +91,8 @@ fs.writeFileSync(
path.join(process.cwd(), 'types', 'generated', 'default-theme.d.ts'),
prettier.format(
`
import { Config } from '../../types'
type CSSDeclarationList = Record<string, string>
export type DefaultTheme = Config['theme'] & { ${defaultThemeTypes} }
export type DefaultTheme = { ${defaultThemeTypes} }
`,
{
semi: false,
Expand Down
7 changes: 4 additions & 3 deletions types/config.d.ts
Expand Up @@ -79,7 +79,7 @@ type Screen = { raw: string } | { min: string } | { max: string } | { min: strin
type ScreensConfig = string[] | KeyValuePair<string, string | Screen | Screen[]>

// Theme related config
interface ThemeConfig {
export interface ThemeConfig {
// Responsiveness
screens: ResolvableTo<ScreensConfig>
supports: ResolvableTo<Record<string, string>>
Expand Down Expand Up @@ -234,8 +234,9 @@ interface ThemeConfig {
transitionDuration: ResolvableTo<KeyValuePair>
willChange: ResolvableTo<KeyValuePair>
content: ResolvableTo<KeyValuePair>
}

// Custom
interface CustomThemeConfig extends ThemeConfig {
[key: string]: any
}

Expand Down Expand Up @@ -358,7 +359,7 @@ interface OptionalConfig {
future: Partial<FutureConfig>
experimental: Partial<ExperimentalConfig>
darkMode: Partial<DarkModeConfig>
theme: Partial<ThemeConfig & { extend: Partial<ThemeConfig> }>
theme: Partial<CustomThemeConfig & { extend: Partial<CustomThemeConfig> }>
corePlugins: Partial<CorePluginsConfig>
plugins: Partial<PluginsConfig>
// Custom
Expand Down

0 comments on commit ffadf2b

Please sign in to comment.