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>;
上面是一项常见的任务,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
?