From df79c139d77aea6403d91af29d39d7c1724c77ff Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Wed, 23 Mar 2022 18:03:15 +0100 Subject: [PATCH 1/4] allow for arbitrary configuration in the `theme` section This is useful for third party plugins otherwise you will get an error. --- types/config.d.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/types/config.d.ts b/types/config.d.ts index 97c4dfd9c4f7..ef2a8f4bc35b 100644 --- a/types/config.d.ts +++ b/types/config.d.ts @@ -218,6 +218,9 @@ interface ThemeConfig { transitionDuration: ResolvableTo willChange: ResolvableTo content: ResolvableTo + + /** Custom */ + [key: string]: any } // Core plugins related config From ad98f8b3c3a1ce9675eb42acf8e7be021a8be5b6 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Wed, 23 Mar 2022 18:04:33 +0100 Subject: [PATCH 2/4] 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(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. --- types/config.d.ts | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/types/config.d.ts b/types/config.d.ts index ef2a8f4bc35b..9460884422e6 100644 --- a/types/config.d.ts +++ b/types/config.d.ts @@ -13,9 +13,53 @@ interface RecursiveKeyValuePair { } type ResolvableTo = T | ((utils: PluginUtils) => T) +type DotNotation = K extends string + ? T[K] extends Record + ? T[K] extends any[] + ? K | `${K}.${DotNotation>}` + : K | `${K}.${DotNotation}` + : K + : never +type Path = DotNotation | keyof T +type PathValue> = P extends `${infer K}.${infer Rest}` + ? K extends keyof T + ? Rest extends Path + ? PathValue + : never + : never + : P extends keyof T + ? T[P] + : never + +// Removes arbitrary values like: { [key: string]: any } +type WithoutStringKey = { [K in keyof T as string extends K ? never : K]: T[K] } + +type Unpack = T extends ResolvableTo + ? { [K in keyof R]: Unpack } + : T extends object + ? { [K in keyof T]: Unpack } + : 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>, 'extend'> + +// This will add additional information purely for code completion. E.g.: +type AugmentedTheme = Expand & { colors: DefaultColors }> + interface PluginUtils { colors: DefaultColors - theme(path: string, defaultValue: unknown): keyof ThemeConfig + + // Dynamic based on (default) theme config + theme

, TDefaultValue>( + path: P, + defaultValue?: TDefaultValue + ): PathValue + // Path is just a string, useful for third party plugins where we don't + // know the resulting type without generics. + theme(path: string, defaultValue?: TDefaultValue): TDefaultValue + breakpoints, 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 From e91848512369c8c641323682cacb3a04b03c7366 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Wed, 23 Mar 2022 18:13:37 +0100 Subject: [PATCH 3/4] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5e649340907..f254b95a8415 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix generation of `div:not(.foo)` if `.foo` is never defined ([#7815](https://github.com/tailwindlabs/tailwindcss/pull/7815)) - Allow for custom properties in `rgb`, `rgba`, `hsl` and `hsla` colors ([#7933](https://github.com/tailwindlabs/tailwindcss/pull/7933)) - Remove autoprefixer as explicit peer-dependency to avoid invalid warnings in situations where it isn't actually needed ([#7949](https://github.com/tailwindlabs/tailwindcss/pull/7949)) +- Types: allow for arbitrary theme values (for 3rd party plugins) ([#7926](https://github.com/tailwindlabs/tailwindcss/pull/7926)) ### Changed From acb49e4b51f92671315f2dc8531c2a1c7111127b Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Thu, 31 Mar 2022 23:40:18 +0200 Subject: [PATCH 4/4] undo all `theme` types, and type it as `theme(path: string): any` Since currently we don't want to investigate time to make the code completion *perfect* (because it might be even impossible to do it properly due to resolving of overrides, extend and deeply nested presets) For now we will provide a way simpler type, which is better than incorrect types. So far we only had types for the default config theme *only*. --- types/config.d.ts | 47 +---------------------------------------------- 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/types/config.d.ts b/types/config.d.ts index 9460884422e6..b033bc319ef6 100644 --- a/types/config.d.ts +++ b/types/config.d.ts @@ -13,53 +13,9 @@ interface RecursiveKeyValuePair { } type ResolvableTo = T | ((utils: PluginUtils) => T) -type DotNotation = K extends string - ? T[K] extends Record - ? T[K] extends any[] - ? K | `${K}.${DotNotation>}` - : K | `${K}.${DotNotation}` - : K - : never -type Path = DotNotation | keyof T -type PathValue> = P extends `${infer K}.${infer Rest}` - ? K extends keyof T - ? Rest extends Path - ? PathValue - : never - : never - : P extends keyof T - ? T[P] - : never - -// Removes arbitrary values like: { [key: string]: any } -type WithoutStringKey = { [K in keyof T as string extends K ? never : K]: T[K] } - -type Unpack = T extends ResolvableTo - ? { [K in keyof R]: Unpack } - : T extends object - ? { [K in keyof T]: Unpack } - : 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>, 'extend'> - -// This will add additional information purely for code completion. E.g.: -type AugmentedTheme = Expand & { colors: DefaultColors }> - interface PluginUtils { colors: DefaultColors - - // Dynamic based on (default) theme config - theme

, TDefaultValue>( - path: P, - defaultValue?: TDefaultValue - ): PathValue - // Path is just a string, useful for third party plugins where we don't - // know the resulting type without generics. - theme(path: string, defaultValue?: TDefaultValue): TDefaultValue - + theme(path: string, defaultValue?: unknown): any breakpoints, 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 @@ -367,4 +323,3 @@ interface OptionalConfig { } export type Config = RequiredConfig & Partial -