我已经为我的Reaction Native应用程序创建了一个函数,它接受一个StyleSheet.NamedStyles<T>
Like对象,但每个样式也可以有一个可选的dark对象,这就是另一种样式本身.该函数返回一个钩子,如果我的应用程序主题是深色的,则该钩子将深色样式合并到基本样式中,并返回它(如果是浅色主题,则只返回基本样式).
以下是一些模拟类型的源代码,仅举个例子.如果这是可能的,我怎么才能让它工作呢?
// Example:
const useStyles = dynamicStyleSheet({
example: {
height: 50,
color: "white",
dark: {
color: "black",
width: 50
}
}
})
// ts might say useStyles is a hook and cannot be called here. it doesn't matter, the type matters.
const styles = useStyles()
type color = typeof styles.example.color // should be "white" | "black". but it's string
type height = typeof styles.example.height // should be 50. but it's number
type width = typeof styles.example.width // should be 50 | undefined. but it's number | undefined
type ImageStyle = {
height?: number
width?: number
objectFit?: "cover" | "contain" | "fill" | "scale-down"
}
type TextStyle = {
fontSize?: number
color?: string
fontStyle?: "normal" | "italic"
textAlign?: "auto" | "left" | "right" | "center" | "justify"
}
type ViewStyle = {
height?: number
width?: number
borderStyle?: "solid" | "dotted" | "dashed"
}
type DynamicNamedStyles<T> = {
[P in keyof T]: (ViewStyle | TextStyle | ImageStyle) & {
dark?: ViewStyle | TextStyle | ImageStyle
}
}
type MergedStyles<T extends DynamicNamedStyles<T>> = {
[P in keyof T]: Omit<T[P], "dark"> & (T[P]["dark"] extends infer D ? Partial<D> : object)
}
type OmitDark<T> = Omit<T, "dark">
type FinalStyles<T extends DynamicNamedStyles<T>> = {
[P in keyof T]: OmitDark<MergedStyles<T>[P]>
}
export function dynamicStyleSheet<T extends DynamicNamedStyles<T> | DynamicNamedStyles<any>>(
styles: T & DynamicNamedStyles<any>
) {
return function useStyles() {
const isDark = useIsDarkScheme()
const lightStyles = objectMap(styles, ({ dark, ...style }) => ({
...style
})) as FinalStyles<T>
const darkStyles = objectMap(styles, ({ dark, ...style }) => ({
...style,
...(dark ?? {})
})) as FinalStyles<T>
return isDark ? darkStyles : lightStyles
}
}
const useIsDarkScheme = () => true // mock
type Dictionary<T> = { [key: string]: T }
export function objectMap<TValue, TResult>(
obj: Dictionary<TValue>,
valSelector: (val: TValue, obj: Dictionary<TValue>) => TResult,
keySelector?: (key: string, obj: Dictionary<TValue>) => string,
ctx?: Dictionary<TValue>
) {
const ret = {} as Dictionary<TResult>
for (const key of Object.keys(obj)) {
const retKey = keySelector ? keySelector.call(ctx ?? null, key, obj) : key
const retVal = valSelector.call(ctx ?? null, obj[key], obj)
ret[retKey] = retVal
}
return ret
}