使用as const构造和infer关键字使用TypeScript进行类型推断

TypeScript允许您自动执行许多任务,而无需使用这种语言,开发人员必须独立解决这些任务。但是,在使用TypeScript时,无需经常使用类型注释。事实是,编译器根据代码执行的上下文在类型推断方面做得很好。这篇文章(我们今天发布的翻译)致力于infer使用关键字和构造的相当复杂的类型推断案例as const



类型推断基础


首先,看一下最简单的类型推断示例。

let variable;

以这种方式声明的变量是type any我们没有给编译器任何有关如何使用它的提示。

let variable = 'Hello!';

在这里,我们声明了一个变量,并立即在其中写入了一个值。TypeScript现在可以猜测到该变量是type string,所以现在我们有了一个完全可以接受的类型化变量。

类似的方法适用于功能:

function getRandomInteger(max: number) {
  return Math.floor(Math.random() * max);
}

在此代码中,我们并不表示该函数getRandomInteger返回数字。但是TypeScript编译器对此非常了解。

泛型中的类型推断


以上概念与通用类型(泛型)有关。如果您想了解更多关于泛型,看看这个这个材料。

创建泛型类型时,您可以做很多有用的事情。类型推断使使用通用类型更加方便并简化了它。

function getProperty<ObjectType, KeyType extends keyof ObjectType>(
  object: ObjectType, key: KeyType
) {
  return object[key];
}

使用上面的泛型函数时,我们不需要显式指定类型。

const dog = {
  name: 'Fluffy'
};
getProperty(dog, 'name');

除其他外,该技术在创建通用React组件时非常有用。这是有关它材料。

使用关键字推断


谈论类型推断时想到的最先进的TypeScript功能之一就是关键字infer

考虑一个例子。创建以下函数:

function call<ReturnType>(
  functionToCall: (...args: any[]) => ReturnType, ...args: any[]
): ReturnType {
  return functionToCall(...args);
}

在此函数的帮助下,调用另一个函数,并将其返回的内容写入常量:

const randomNumber = call(getRandomInteger, 100);

前面的表达式使我们能够获得getRandomInteger接收输入的函数作为返回给它的随机整数的上限100所返回的结果。确实,有一个小问题。事实是,没有什么可以阻止我们忽略函数参数的类型getRandomInteger

const randomNumber = call(getRandomInteger, '100'); //   

由于TypeScript在高阶函数中支持散布和休息参数,因此我们可以解决以下问题:

function call<ArgumentsType extends any[], ReturnType>(
  functionToCall: (...args: ArgumentsType) => ReturnType, ...args: ArgumentsType
): ReturnType {
  return functionToCall(...args);
}

现在我们已经指出该函数call可以处理任何形式的参数数组,并且这些参数必须与传递给它的函数的期望相匹配。

现在,让我们再试一次不正确的函数调用:

const randomNumber = call(getRandomInteger, '100');

这将导致错误消息:

Argument of type ‘”100″‘ is not assignable to parameter of type ‘number’.

实际上,按照上述步骤,我们仅创建了一个元组。TypeScript中的元组是固定长度的数组,其值类型是已知的,但不必相同。

type Option = [string, boolean];
const option: Option = ['lowercase', true];

关键字功能推断


现在,让我们想象一下我们的目标不是获取函数返回的内容,而仅仅是获取有关返回给它的数据类型的信息。

type FunctionReturnType<FunctionType extends (...args: any) => ?> = ?;

上面的类型尚未准备就绪。我们需要解决如何确定返回值的问题。您可以在此处手动描述所有内容,但这违背了我们的目标。

type FunctionReturnType<ReturnType, FunctionType extends (...args: any) => ReturnType> = ReturnType;
FunctionReturnType<number, typeof getRandomInteger>;

代替我们自己执行此操作,我们可以要求TypeScript输出返回类型。关键字infer只能在条件类型中使用。这就是为什么我们的代码有时会有些混乱。

type FunctionReturnType<FunctionType extends (args: any) => any> = FunctionType extends (...args: any) => infer ReturnType ? ReturnType : any;

这是这段代码中发生的事情:

  • 它说FunctionType在这里扩展(args: any) => any
  • 我们指出FunctionReturnType这是一个条件类型。
  • 我们检查它是否扩展FunctionType (...args: any) => infer ReturnType

完成所有这些操作后,我们可以提取任何函数的返回类型。

FunctionReturnType<typeof getRandomInteger>; // number

上面是一项常见的任务,TypeScript具有一个内置实用程序ReturnType,旨在解决此问题。

构造为const


与类型推断有关的另一个问题是关键字之间的区别,const并且let在声明常量和变量时使用它们。

let fruit = 'Banana';
const carrot = 'Carrot';

变量fruit-具有类型string。这意味着任何字符串值都可以存储在其中。

常量carrot是字符串文字。可以将其视为子类型的示例string。在此PR中给出了字符串文字以下描述:“类型字符串文字是一种类型,其预期值是一个字符串,其文本内容等于该字符串文字的相同内容。”

可以更改此行为。 TypeScript 3.4引入了一个有趣的新功能,称为const断言,它提供了使用构造的功能as const。它的用法如下所示:

let fruit = 'Banana' as const;

现在fruit是字符串文字。as const当某些实体需要变得不可变时,该设计也很方便。考虑以下对象:

const user = {
  name: 'John',
  role: 'admin'
};

在JavaScript中,关键字const表示您不能覆盖constant中存储的内容user但是,另一方面,您可以更改以该常数记录的对象的内部结构。

现在,该对象存储以下类型:

const user: {
  name: string,
  role: string
};

为了使系统将这个对象视为不可变的,可以使用以下设计as const

const user = {
  name: 'John',
  role: 'admin'
} as const;

现在,类型已更改。字符串变成了字符串文字,而不是普通的字符串。但不仅如此,它已经改变了。现在属性是只读的:

const user: {
  readonly name: 'John',
  readonly role: 'admin'
};

当使用数组时,我们面前将出现更强大的可能性:

const list = ['one', 'two', 3, 4];

此数组的类型为(string | number)[]使用此数组,as const可以将其转换为元组:

const list = ['one', 'two', 3, 4] as const;

现在,此数组的类型如下所示:

readonly ['one', 'two', 3, 4]

所有这些都适用于更复杂的结构。考虑一下Anders Halesberg在TSConf 2019演讲中的例子

const colors = [
  { color: 'red', code: { rgb: [255, 0, 0], hex: '#FF0000' } },
  { color: 'green', code: { rgb: [0, 255, 0], hex: '#00FF00' } },
  { color: 'blue', code: { rgb: [0, 0, 255], hex: '#0000FF' } },
] as const;

colors现在, 我们的数组已受到保护,免受更改,并且其元素也已免受更改:

const colors: readonly [
    {
        readonly color: 'red';
        readonly code: {
            readonly rgb: readonly [255, 0, 0];
            readonly hex: '#FF0000';
        };
    },
    /// ...
]

摘要


在本文中,我们研究了在TypeScript中使用高级类型推断机制的一些示例。这里使用关键字infer和机制as const这些工具在某些特别困难的情况下非常有用。例如,当您需要使用不可变实体时,或以功能样式编写程序时。如果您想继续熟悉此主题,请阅读材料。

亲爱的读者们!您是否在TypeScript中使用关键字infer和构造as const


All Articles