在TypeScript

问题描述

我在TypeScript的React Native项目中有一个强类型的样式对象:

const styles = StyleSheet.create({
  container:{
    backgroundColor: 'red',flex: 1
  },});

这正常工作,因为StyleSheet.create期望具有以下定义的目标值ViewStyleTextStyleImageStyle的任意命名键:

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是任意键,并且backgroundColorViewStyle中定义为具有类型ColorValue的值,该值定义为string | OpaqueColorValue,其中OpaqueColorValue是唯一的符号。

但是,无论我以前在原始类型中接受过ColorValue的何处,我都在构建一个具有以下类型签名的函数

ColorValue | { light: ColorValue,dark: ColorValue }

函数中同时提供了名为lightdark的参数,我将像这样使用它:

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} }。

我该如何实现?

解决方法

我认为您可能会受益于这样一些conditionalmapped 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;
    };
} */

我想看起来像您想要的。您也许可以变得更加聪明,并让输出类型特别是从lightdark中选取的值的类型(所以string而不是ColorValue),但这会更复杂,可能没有必要,所以我就到此为止。

Playground link to code

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...