问题描述
我在TypeScript的React Native项目中有一个强类型的样式对象:
const styles = StyleSheet.create({
container:{
backgroundColor: 'red',flex: 1
},});
这正常工作,因为StyleSheet.create
期望具有以下定义的目标值ViewStyle
,TextStyle
和ImageStyle
的任意命名键:
export namespace StyleSheet {
type NamedStyles<T> = { [P in keyof T]: ViewStyle | TextStyle | ImageStyle };
/**
* Creates a StyleSheet style reference from the given object.
*/
export function create<T extends NamedStyles<T> | NamedStyles<any>>(styles: T | NamedStyles<T>): T;
[...]
}
container
是任意键,并且backgroundColor
在ViewStyle
中定义为具有类型ColorValue
的值,该值定义为string | OpaqueColorValue
,其中OpaqueColorValue
是唯一的符号。
但是,无论我以前在原始类型中接受过ColorValue
的何处,我都在构建一个具有以下类型签名的函数:
ColorValue | { light: ColorValue,dark: ColorValue }
在函数中同时提供了名为light
或dark
的参数,我将像这样使用它:
const styles = myFunction({
container:{
backgroundColor: {
light: 'red',dark: 'blue'
},},'dark');
它将选择适当的密钥,例如它会返回:
{
container:{
backgroundColor: 'blue' //as we picked the 'dark' key from original input
flex: 1
}
}
简而言之,我希望函数的输入类型接受StyleSheet.create
接受的任何内容,并且除此之外,还要在接受{{1的任何地方接受{light: ColorValue,dark: ColorValue}
}}。然后,它会选择适当的键,然后在内部调用ColorValue
并返回结果。
我编写了该函数并且它起作用了,但是我正在努力使它使用IntelliSense和linter中的StyleSheet.create
键接受对象(当然,不强制转换为light/dark
或{{1} }。
我该如何实现?
解决方法
我认为您可能会受益于这样一些conditional,mapped types:
type DeepReplaceSupertype<T,S,D> = [S] extends [T] ? D : {
[K in keyof T]: DeepReplaceSupertype<T[K],D>
}
type DeepReplaceSubtype<T,D> = [T] extends [S] ? D : {
[K in keyof T]: DeepReplaceSubtype<T[K],D>
}
这个想法是DeepReplaceSupertype<T,D>
的类型为T
,源类型为S
,目标类型为D
,它检查:S
是否为可分配给T
,然后将其替换为D
。否则,递归地遍历T
,并以相同的方式替换所有属性和子属性。 DeepReplaceSubtype<T,D>
与之类似,但是它检查T
是否可分配给S
。
然后您的函数签名如下所示:
interface Shade { light: ColorValue,dark: ColorValue }
export declare function myFunction<
T extends DeepReplaceSupertype<NamedStyles<any>,ColorValue,Shade>>(
styles: T,shade: 'light' | 'dark'
): DeepReplaceSubtype<T,Shade,ColorValue>;
含义:接受T
类型的任何内容,并将其中的NameStyles
替换为接受ColorValue
的子属性,并将其更改为Shade
。然后,返回值应为T
,并替换属于Shade
子类型的所有子属性,并将其返回到ColorValue
。它应该像这样工作:
const styles = StyleSheet.myFunction({
container: {
backgroundColor: {
light: 'red',dark: 'blue'
},flex: 1
},},'dark');
/* const styles: {
container: {
backgroundColor: ColorValue;
flex: number;
};
} */
我想看起来像您想要的。您也许可以变得更加聪明,并让输出类型特别是从light
或dark
中选取的值的类型(所以string
而不是ColorValue
),但这会更复杂,可能没有必要,所以我就到此为止。