Improve resolveConfig return type: merge themes (#12272)

* 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>
This commit is contained in:
Nikita Gaidakov 2023-10-27 14:46:02 +01:00 committed by Jordan Pittman
parent ac171f02c7
commit ffadf2ba4b
4 changed files with 28 additions and 8 deletions

View File

@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Dont 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

25
resolveConfig.d.ts vendored
View File

@ -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>

View File

@ -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,

7
types/config.d.ts vendored
View File

@ -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>>
@ -234,8 +234,9 @@ interface ThemeConfig {
transitionDuration: ResolvableTo<KeyValuePair>
willChange: ResolvableTo<KeyValuePair>
content: ResolvableTo<KeyValuePair>
}
// Custom
interface CustomThemeConfig extends ThemeConfig {
[key: string]: any
}
@ -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