我试图在类型脚本中创建一个具有适当类型返回的异步函数.该函数将有一个布尔参数来确定函数的返回类型.我在网上搜索,似乎使用类型条件是可行的.

我实现的代码大致如下所示:

type Foo = {
    propA: string
    propB: string
}

type Bar = Omit<Foo, 'propB'> & {propC: string}

type ConditionalFooBar<T extends boolean> = T extends true? Foo:Bar

async function mainFunction<T extends boolean>(param:T) : Promise<ConditionalFooBar<T>>{
    // Some function awaits happened here
    if(param===true){
        return {
            propA: "a string",
            propB: "a string"
        }
    }
    return {
        propA: "a string",
        propC: "a string"
    }
}

对于这段代码,TypeScrip编译器在第一个返回语句中抱怨如下:

Type '{ propA: string; propB: string; }' is not assignable to type 'ConditionalFooBar<T>'.ts(2322)

我该如何解决这个问题?

推荐答案

目前(从类型脚本5.4开始),不可能以编译器可以准确地验证为类型安全的方式来实现返回conditional typesgeneric functions.在microsoft/TypeScript#33912有一个长期开放的特性请求,希望有更好的东西,看起来这个问题是microsoft/TypeScript#57475的TypeScrip5.5路由图的一部分,在microsoft/TypeScript#56941有一个拉请求来解决它.如果拉请求或类似的请求被合并,那么您的代码可能会突然开始工作,就像您编写的代码一样,没有错误.

除非发生这种情况,否则,如果您希望在运行时保持函数不变,您将需要使用类似type assertions的代码来防止编译器错误, Select 不使用可能的漏报进行判断,而不是使用漏报进行过于严格的判断:

    async function mainFunction<T extends boolean>(param: T): Promise<ConditionalFooBar<T>> {
      if (param === true) {
        return {
          propA: "a string",
          propB: "a string"
        } as ConditionalFooBar<T>
      }
      return {
        propA: "a string",
        propC: "a string"
      } as ConditionalFooBar<T>
    }
  }

如果您现在需要编译器验证的类型安全,则需要重构,从使用条件类型转向使用indexed access types,其中您的操作是根据属性查找而不是if/else块编写的.例如:

    interface X {
      true: Foo;
      false: Bar;
    }

    async function mainFunction<T extends boolean>(param: T) {
      const x: X = {
        get true() {
          return {
            propA: "a string",
            propB: "a string"
          }
        },
        get false() {
          return {
            propA: "a string",
            propC: "a string"
          }
        }
      }
      return x[`${param}`]
    }

在这里,您可以看到X接口只是从string "true"Foo以及从string "false"Bar的映射.在函数内部,我们有一个X类型的值x,其中getters表示我们不必提前计算truefalse分支.我们用template literal string来索引X,其中truefalse被序列化为"true""false".

如果判断mainFunction()的调用签名,则为:

// function mainFunction<T extends boolean>(param: T): Promise<X[`${T}`]>

其返回索引访问,其中关键字是对应于序列化的booleantemplate literal type.您可以看到它以您想要的方式工作:

    const a = mainFunction(true);
    //    ^? Promise<Foo>
    const b = mainFunction(false);
    //    ^? Promise<Bar>

因此,当您调用编译器时,它会自动推断您所期望的类型.但这个版本肯定比最初的实现更奇怪.仅仅为了类型安全,在作为一对getter实现的对象中查找序列化的布尔键可能不值得.

Playground link to code

Typescript相关问答推荐

如何在不使用变量的情况下静态判断运算式的类型?

如何根据数据类型动态注入组件?

使用`renderToPipeableStream`时如何正确恢复服务器端Apollo缓存?

我可以为情态车使用棱角形的路由守卫吗?

如何使用WebStorm调试NextJS的TypeScrip服务器端代码?

使用Redux Saga操作通道对操作进行排序不起作用

为什么我的查询钩子返回的结果与浏览器的网络选项卡中看到的结果不同?

如何从MAT-FORM-FIELD-MAT-SELECT中移除焦点?

将带有样式的Reaction组件导入到Pages文件夹时不起作用

如何创建由空格连接的多个字符串的类型

在组件卸载时try 使用useEffect清除状态时发生冲突.react +打字

如何在try 运行独立的文字脚本Angular 项目时修复Visual Studio中的MODULE_NOT_FOUND

递归生成常量树形 struct 的键控类型

在REACT查询中获取未定义的isLoading态

如何键入';v型';

类型分布在第一个泛型上,但不分布在第二个泛型上

记录的子类型<;字符串,X>;没有索引签名

Typescript 是否可以区分泛型参数 void?

如何确定剧作家中给定定位器的值?

在Typescript 中,是否有一种方法来定义一个类型,其中该类型是字符串子集的所有可能组合?