首页 > 解决方案 > Typescript 推断的对象字面量类型扩展如何工作?

问题描述

我已经阅读了关于 typescript 如何扩大推断类型的信息,但我仍然不确定这里发生了什么:

type Def = {
  'T': { status: 5, data: {r: 'm'}},
}

function route<S extends keyof Def>
   (route: S, handler: () => Promise<Def[S]>) { }

route('T', async function () {
   return { status: 5, data: {r: 'm'} };
});

这在 Typescript 操场上运行良好,但不应该引发错误,因为 Typescript 推断第二个参数的类型route()() => {status: number, data: {r: string}}?

我问是因为我对这段代码有疑问。传入的函数Router::get被推断为() => {status: number}而不是() => {status: 200}. 奇怪的是,当我删除POST路线时没有引发错误(显示在这里)。

标签: typescript

解决方案


这个简单的示例确实按预期工作,并且此处不会发生文字类型扩展 - 函数route()是通用的,并且S首先被推断,因为您在没有指定其通用类型参数的情况下调用它S

route('T', async function () {
   return { status: 5, data: {r: 'm'} };
});

所以,首先,编译器必须S从传递的实际参数的类型中推断出来。

通过查看第一个参数 literal 'T',它确定S为 literal type 'T'

因此,第二个参数的返回类型立即() => Promise<Def[S]>固定为定义为的对象字面量类型Def['T']。然后编译器不需要推断异步函数的返回类型,它只检查它是否符合Def['T'](确实如此)。

为什么这在我不知道的更复杂的代码中不起作用,但我怀疑索引签名会ApiDefBase阻止类型推断的影响Path- 如果您将类似的索引签名添加到简单示例中,您将开始收到相同的错误:

type DefBase = { [p in string]: {}}

interface Def extends DefBase {
  'T': { status: 5, data: {r: 'm'}},
}

function route<S extends keyof Def>
   (route: S, handler: () => Promise<Def[S]>) { }

route('T', async function () {
   return { status: 5, data: {r: 'm'} };
});

Argument of type '() => Promise<{ status: number; data: { r: string; }; }>' is not assignable to parameter of type '() => Promise<{ status: 5; data: { r: "m"; }; }>'.
  Type 'Promise<{ status: number; data: { r: string; }; }>' is not assignable to type 'Promise<{ status: 5; data: { r: "m"; }; }>'.
    Type '{ status: number; data: { r: string; }; }' is not assignable to type '{ status: 5; data: { r: "m"; }; }'.
      Types of property 'status' are incompatible.
        Type 'number' is not assignable to type '5'.

现在,为什么添加索引签名会对类型推断产生这种影响——我不知道。


推荐阅读