TypeScript 中的 Infer,强大又强大

2025-06-08

TypeScript 中的 Infer,强大又强大

让我们来谈谈inferTypeScript 中的关键字。我相信很多开发者都对它感到困惑infer。我什么时候应该使用它?它是如何工作的?它的作用是什么?

困惑的开发者

我们先从简单的事情开始。接下来的案例将帮助我们理解 的概念infer。好的,请看下面的代码:

let a = 10;
Enter fullscreen mode Exit fullscreen mode

TypeScript 知道一种类型a——数字。这使得开发人员不必到处都写这种类型。TypeScript 会处理好这个问题。顺便说一下,如果要用到的话,会用到另一种类型const。自己尝试一下吧。我在评论区等着你的解释。这是一个与读者的小小互动。

好的,我们刚刚发现,TypeScript 非常智能。如果一个值符合特定条件,TypeScript 就能判断它的类型。就像这样:

if (typeof a === 'number') {
  return number;
} else {
  return ???;
}
Enter fullscreen mode Exit fullscreen mode

(什么???意思?)遇到这种never类型了。看起来没什么用,谁会用呢?在 TypeScript 中,never它被视为无值类型。我们有没有不返回值的函数(undefined 是正确的值)?顺便说一下,我们可以创建这样的函数。例如:

function foo() {
  throw new Error('error');
}
Enter fullscreen mode Exit fullscreen mode

检查的返回类型foo

其实never类型并非毫无用处。我们回到那个数字的例子。我们可以never在 else 条件中使用:

if (typeof a === 'number') {
  return number;
} else {
  return never;
}

// or

typeof a === 'number' ? number : never;
Enter fullscreen mode Exit fullscreen mode

当然,TypeScript 的代码库里没有这样的代码。这只是 TypeScript 工作方式的一个模型。

TypeScript 中有一个与 if/else 等效的语句。我说的是这个extends关键字:

type NumberFromType<T> = T extends number 
  ? number 
  : never;
Enter fullscreen mode Exit fullscreen mode

嗯,有新东西了—— <T>。我们把它想象成一个类型的盒子。T可以是任何类型。只要它在语句中的任何地方被定义,我们就会把它放进那个盒子里。经典的例子:

function test<T>(x: T): T {
  return x;
}

test(10);
Enter fullscreen mode Exit fullscreen mode

如果你这样调用它test(10);,TypeScript 就会定义Tnumber,因为xnumber。我知道,这个解释很简单。但现在就够了。好的,我们可以继续了。

让我们回到NumberFromType. ,T extends number这意味着可以安全地假设 类型的值T也是 类型number。例如, 10 扩展了 ,number因为let a: number = 10它是类型安全的。嗯,如果我们将一个字符串传递给NumberFromType<T>? ,会发生什么?

type A = NumberFromType<'10'>;

// It will be treated like this:

type NumberFromType<'10'> = '10' extends number ? number : never; 
Enter fullscreen mode Exit fullscreen mode

因此,never类型中会有A,因为字符串不是从数字扩展而来的。但是如果我们也需要处理字符串呢?我们可以将一个条件放在另一个条件中。像这样:

type StringOrNumberFromType<T> = T extends string
  ? string
  : T extends number
    ? number
    : never
Enter fullscreen mode Exit fullscreen mode

瞧,它适用于字符串和数字)因此,您可以更深入地了解每种类型并添加更多条件。

infer 关键字

终于准备好了infer!首先,该infer关键字只能在条件类型中使用。原因很简单——你不能在其他地方使用它 =) 该关键字在条件类型之外没有任何含义。

好的,我们尝试将一个数字数组传递给NumberFromType。结果never显然是 。数字数组并非从 number 扩展而来。但如果我们需要获取数组项的类型怎么办?可以像这样:

type ArrayItemType<T> = T extends ...
Enter fullscreen mode Exit fullscreen mode

这条语句接下来应该做什么?我们需要检查一下,它T是一个数组:

type ArrayItemType<T> = T extends [] ? ... : never;
Enter fullscreen mode Exit fullscreen mode

我刚刚完成了条件,就像在 中一样NumberFromType。但主要问题还没有解决,因为我们需要数组项的类型。顺便说一下,我们可以为数字数组写一个类型,例如number[]Array<number>。任何其他类型都可以放在<>括号内。因此,我们的条件可以这样写:

type ArrayItemType<T> = T extends Array<ITEM_TYPE> 
  ? ITEM_TYPE 
  : never;
Enter fullscreen mode Exit fullscreen mode

好的,好多了!但是ITEM_TYPE没有定义。它还没有被推断出来。是的,我们需要 TypeScript 来推断类型。我们可以让 TypeScript 来做这件事:

type ArrayItemType<T> = T extends Array<infer ITEM_TYPE> 
  ? ITEM_TYPE 
  : never;
Enter fullscreen mode Exit fullscreen mode

这意味着,如果T从某种类型扩展而来,那么,TypeScript,如果您能推断出项目的类型并将其作为结果返回,Array那对您来说真的太好了。T's

一般来说,我们可以说,inferTypeScript 中的关键字和条件类型允许我们采用一种类型并隔离它的任何部分以供以后使用。

有一些现实生活中的例子。

不承诺

export type Unpromisify<T> = T extends Promise<infer Result> 
  ? Result 
  : never;
Enter fullscreen mode Exit fullscreen mode

顾名思义,它返回一个 PromiseUnpromisify<T>结果。另外,如果您使用 TypeScript 4.5 或更高版本,则可以使用内置类型。typescriptlang.orgAwaited有一些示例Awaited

组件属性

在 React 中,我们经常需要访问组件的 prop 类型。为此,React 提供了一种实用程序类型,用于访问 prop 类型,该类型由infer名为 的关键字提供支持。您可以在DefinitelyTyped 仓库ComponentProps中找到其完整定义

type ComponentProps<
    T extends keyof JSX.IntrinsicElements | JSXElementConstructor<any>
  > =
        T extends JSXElementConstructor<infer P>
            ? P
            : T extends keyof JSX.IntrinsicElements
                ? JSX.IntrinsicElements[T]
                : {};
Enter fullscreen mode Exit fullscreen mode

在检查到它T是一个 React 组件 ( T extends keyof JSX.IntrinsicElements | JSXElementConstructor<any>) 之后,它会推断出它的 props 并返回它们 ( T extends JSXElementConstructor<infer P> ? P)。尾部是一个简单的 React 元素。

结论

infer关键字是一个强大的工具,它允许我们从任何复杂类型中解包并存储类型。它就像一个类型拆箱操作。所以,这个关键字背后并没有什么神秘之处。

如果你想挑战一下你的 TypeScript 技能,我推荐你参加type-challenges。我确信这篇文章对你来说非常有用。尽情享受吧!

鏂囩珷鏉ユ簮锛�https://dev.to/artemmalko/infer-in-typescript-the-great-and-powerful-5cch
PREV
嘲弄敏捷 嘲笑方法论 严肃地说 快餐管道 完美的美食 那又怎样?
NEXT
使用 Vue 创建无需任何 Node 模块的 SPA