作者选择了COVID-19 救济基金来接受捐赠,作为Write for DOnations计划的一部分。
介绍
TypeScript是JavaScript语言的扩展,它使用 JavaScript 的运行时和编译时类型检查器。这种组合允许开发人员使用完整的 JavaScript 生态系统和语言功能,同时还添加了可选的静态类型检查、枚举数据类型、类和接口。这些特性为开发人员提供了 JavaScript 动态特性的灵活性,但也允许更可靠的代码库,其中类型信息可以在编译时用于检测可能导致错误或其他意外行为的问题。
额外的类型信息还提供了更好的代码库文档和文本编辑器中改进的IntelliSense(代码完成、参数信息和类似的内容辅助功能)。团队成员可以准确地确定任何变量或函数参数所期望的类型,而无需通过实现本身。
本教程将介绍类型声明和 TypeScript 中使用的所有基本类型。它将引导您浏览具有不同代码示例的示例,您可以在自己的 TypeScript 环境或TypeScript Playground 中学习,后者是一个允许您直接在浏览器中编写 TypeScript 的在线环境。
先决条件
要学习本教程,您需要:
- 您可以在其中执行 TypeScript 程序以跟随示例进行操作的环境。要在本地计算机上进行设置,您需要以下内容。
- 这两个节点和NPM(或丝安装才能运行的开发环境,手柄的打字稿相关的软件包)。本教程使用 Node.js 版本 14.3.0 和 npm 版本 6.14.5 进行了测试。要在 macOS 或 Ubuntu 18.04 上安装,请按照如何在 macOS 上安装 Node.js 和创建本地开发环境或如何在 Ubuntu 18.04 上安装 Node.js 的使用 PPA 安装部分中的步骤进行操作。如果您使用的是适用于 Linux的Windows 子系统 (WSL),这也适用。
- 此外,您将需要
tsc
在您的机器上安装TypeScript 编译器 ( )。为此,请参阅TypeScript 官方网站。
- 如果您不想在本地机器上创建 TypeScript 环境,您可以使用官方的TypeScript Playground进行操作。
- 您将需要足够的 JavaScript 知识,尤其是 ES6+ 语法,例如解构、rest 运算符和import/exports。如果您需要有关这些主题的更多信息,建议阅读我们的如何在 JavaScript 中编码系列。
- 本教程将参考支持 TypeScript 并显示内联错误的文本编辑器的各个方面。这对于使用 TypeScript 不是必需的,但确实可以更多地利用 TypeScript 的功能。为了获得这些好处,您可以使用像Visual Studio Code这样的文本编辑器,它开箱即用地完全支持 TypeScript。您还可以在TypeScript Playground 中尝试这些好处。
本教程中显示的所有示例都是使用 TypeScript 4.2.2 版创建的。
在 TypeScript 中声明变量类型
当在JavaScript中,这是一个纯粹的动态语言编写代码,你不能指定数据类型的变量。您创建变量并为其分配值,但不指定类型,如下所示:
const language = {
name: "JavaScript"
};
在此代码块中,language
有一个对象,该对象保存属性的字符串值name
。其值类型language
及其属性没有明确设置,如果未来的开发人员不知道什么类型的值language
引用,这可能会在以后造成混乱。
TypeScript 的主要优点是严格的类型系统。一个静态类型语言是其中的变量的类型在编译时是已知的。在本节中,您将尝试使用 TypeScript 指定变量类型的语法。
类型是您直接在代码中编写的额外信息。TypeScript 编译器使用这些额外信息来强制正确使用不同的值,具体取决于它们的类型。
想象一下使用动态语言(例如 JavaScript)并使用string
变量,就像它是number
. 当您没有strict
单元测试时,可能的错误只会在运行时出现。如果使用 TypeScript 可用的类型系统,编译器将不会编译代码,而是给出错误,如下所示:
OutputThe right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. (2363)
要在 TypeScript 中声明具有特定类型的变量,请使用以下语法:
declarationKeyword variableName: Type
declarationKeyword
将类似于let
, var
, 或const
。后面跟着变量名、冒号 ( :
) 和该变量的类型。
您在 TypeScript 中编写的任何代码在某种程度上都已经在使用类型系统,即使您没有指定任何类型。以这段代码为例:
let language = 'TypeScript';
在 TypeScript 中,这与以下含义相同:
let language: string = 'TypeScript';
在第一个示例中,您没有将language
变量的类型设置为string
,但是 TypeScript 推断了该类型,因为您在声明时分配了一个字符串值。在第二个示例中,您将language
变量的类型显式设置为string
。
如果您使用const
代替let
,则如下所示:
const language = 'TypeScript';
在这种情况下,TypeScript 会使用字符串字面量TypeScript
作为变量的类型,就好像你是这样输入的一样:
const language: 'TypeScript' = 'TypeScript';
TypeScript 这样做是因为,在使用时const
,您不会在声明后为变量分配新值,因为这样做会引发错误。
注意:如果您使用支持 TypeScript 的编辑器,将光标悬停在变量上将显示每个变量的类型信息。
如果您显式设置变量的类型,然后使用不同的类型作为其值,则 TypeScript 编译器 ( tsc
) 或您的编辑器将显示错误2322
。尝试运行以下命令:
const myNumber: number = 'look! this is not a number :)';
这将产生以下错误:
OutputType 'string' is not assignable to type 'number'. (2322)
现在您已经尝试在 TypeScript 中设置变量的类型,下一节将展示 TypeScript 支持的所有基本类型。
TypeScript 中使用的基本类型
TypeScript 有多种基本类型,可在构建更复杂的类型时用作构建块。在以下部分中,您将检查这些类型中的大多数。请注意,您在本节中创建的大多数变量都可以省略它们的类型,因为 TypeScript 可以推断它们,但是出于学习目的,您明确说明了类型。
string
该类型string
用于文本数据类型,如字符串文字或模板字符串。
试试下面的代码:
const language: string = 'TypeScript';
const message: string = `I'm programming in ${language}!`;
在这个代码块中,language
和message
都被分配了string
类型。模板文字仍然是一个字符串,即使它是动态确定的。
由于字符串在 JavaScript 编程中很常见,这可能是您最常使用的类型之一。
boolean
该类型boolean
用于表示true
或false
。
尝试以下块中的代码:
const hasErrors: boolean = true;
const isValid: boolean = false;
由于hasErrors
和isValid
被声明为布尔值,因此只能为它们分配值true
和false
。请注意,truthy和falsy值不会转换为它们的布尔值,如果与这些变量一起使用,则会抛出错误。
number
该类型number
用于表示整数和浮点数,如下所示:
const pi: number = 3.14159;
const year: number = 2021;
这是 JavaScript 开发中经常使用的另一种常见类型,因此该声明将在 TypeScript 中很常见。
bigint
typebigint
是一种可以在面向 ES2020 时使用的类型。它用于表示BigInt
,这是一种用于存储大于 的整数的新数据类型2^53
。
试试下面的代码:
const bigNumber: bigint = 9007199254740993n;
注意:如果此代码抛出错误,则可能是 TypeScript 未设置为 target ES2020
。这可以在您的tsconfig.json
文件中更改。
如果您正在处理大于2^53
或使用某些数学库的数字,bigint
则将是一个常见的类型声明。
symbol
该symbol
类型用于代表Symbol
原始值。这将创建一个唯一的、未命名的值。
使用Symbol()
构造函数运行以下代码:
const mySymbol: symbol = Symbol('unique-symbol-value');
这些值的唯一性可用于避免引用冲突。有关 JavaScript 中符号的更多信息,请阅读Mozilla 开发人员网络 (MDN) 上的符号文章。
数组
在 TypeScript 中,数组的类型是基于它们预期具有的元素。有两种输入数组的方法:
- 附加
[]
到数组元素的预期类型。例如,如果你想输入一个包含多个number
值的数组,你可以这样做:
const primeNumbers: number[] = [2, 3, 5, 7, 11];
如果你给这个数组分配了一个字符串值,TypeScript 会给你一个错误。
- 使用泛型,其中是该数组中元素的预期类型。使用前面的例子,它会变成这样:
Array<T>
T
const primeNumbers: Array<number> = [2, 3, 5, 7, 11];
两种方式都是相同的,所以选择一种并尝试仅使用该格式来表示数组。这将使代码库保持一致,这通常比选择一种风格更重要。
在 TypeScript 中使用保存数组的变量的一个重要方面是,大多数时候你必须输入它们。试试下面的代码:
const myArray = [];
TypeScript 无法推断此数组所需的正确类型。相反,它使用any[]
,这意味着任何东西的数组。这不是类型安全的,并且可能会在您的代码中引起混乱。
为了使您的代码更健壮,建议明确说明数组的类型。例如,这将确保数组具有数字元素:
const myArray: number[] = [];
这样,如果您尝试将无效值推送到数组,TypeScript 将产生错误。试试下面的代码:
const myArray: number[] = [];
myArray.push('some-text');
TypeScript 编译器将显示错误2345
:
OutputArgument of type 'string' is not assignable to parameter of type 'number'. (2345)
元组
元组是具有特定数量元素的数组。一个常见的用例是以[x, y]
. 如果您正在使用React并使用Hooks,那么大多数 Hooks 的结果也是一个元组,例如const [isValid, setIsValid] = React.useState(false)
.
要键入元组,与键入数组时相反,您将元素的类型包装在 a 中[]
,用逗号分隔它们。想象一下,您正在创建一个包含元素类型的文字数组:
const position: [number, number] = [1, 2];
如果您尝试传递的元素数量少于或多于元组预期的数量,TypeScript 编译器将显示错误2322
。
以下面的代码为例:
const position: [number, number] = [1, 2, 3];
这将产生以下结果:
OutputType '[number, number, number]' is not assignable to type '[number, number]'.
Source has 3 element(s) but target allows only 2. (2322)
any
在某些情况下,指定值的类型可能太难了,例如该值是否来自第三方库或最初未使用 TypeScript 编写的代码。在以小步骤将 JavaScript 代码库迁移到 TypeScript 时,这尤其常见。在这些场景中,可以使用称为 的特殊类型any
,这意味着任何类型。Usingany
意味着选择退出类型检查,与使 TypeScript 编译器忽略该值相同。
取以下代码块:
let thisCanBeAnything: any = 12345;
thisCanBeAnything = "I can be anything - Look, I'm a string now";
thisCanBeAnything = ["Now I'm an array - This is almost like pure JavaScript!"];
这些声明都不会在 TypeScript 中给出错误,因为类型被声明为any
.
注意:大多数时候,如果可以的话,应该避免使用any
. 使用它失去了 TypeScript 的主要好处之一:具有静态类型的代码。
unknown
该unknown
类型就像该类型的类型安全对应物any
。您可以使用unknown
,当你想输入的东西,你不能确定的值,但仍然要确保使用该值的任何代码中使用它之前,正确检查的类型。这对于库作者的库中的函数可能会接受来自用户的范围广泛的值并且不想明确键入值的库作者很有用。
例如,如果您有一个名为 的变量code
:
let code: unknown;
然后在程序的后面,您可以为该字段分配不同的值,例如35
( number
),或完全不相关的值,例如数组甚至对象。
注意:您正在使用let
是因为您要为该变量分配一个新值。
稍后在同一代码中,您可以设置code
为一个数字:
code = 35;
但后来你可以将它分配给一个数组:
code = [12345];
你甚至可以将它重新分配给一个对象:
code = {};
如果稍后在代码中您想将该值与其他值进行比较number
,例如:
const isCodeGreaterThan100 = code > 100;
TypeScript 编译器将显示错误2571
:
OutputObject is of type 'unknown'. (2571)
发生这种情况是因为code
需要是number
此比较的unknown
类型,而不是类型。当使用 type 值执行任何操作时unknown
,TypeScript 需要确保类型是它期望的类型。这样做的一个例子是使用typeof
JavaScript 中已经存在的运算符。检查以下代码块:
if (typeof code === 'number') {
const isCodeGreaterThan100 = code > 60;
// ...
} else {
throw new Error('Invalid value received as code');
}
在此示例中,您正在code
使用typeof
运算符检查是否为数字。当你这样做时,TypeScript 将把你的变量的类型强制到number
那个if
块内,因为在运行时if
块内的代码只有在code
当前设置为一个数字时才会被执行。否则,您将抛出一个 JavaScript 错误,指出传递的值无效。
要了解unknown
和any
类型之间的区别,您可以将其unknown
视为“我不知道该值的类型”和any
“我不在乎该值持有什么类型”。
void
您可以使用该void
类型将有问题的变量定义为根本不包含任何类型。如果将不返回任何值的函数的结果分配给变量,则该变量的类型将为void
。
取以下代码:
function doSomething() {};
const resultOfVoidFunction: void = doSomething();
您很少需要void
直接在 TypeScript 中使用该类型。
null
和 undefined
null
和undefined
TypeScript 中的值有自己独特的类型,它们被称为同名:
const someNullField: null = null;
const someUndefinedField: undefined = undefined;
这些在创建您自己的自定义类型时特别有用,这将在本系列的后面部分进行介绍。
never
该never
类型是永远存在的值的类型。例如,假设您创建了一个数字变量:
const year: number = 2021;
如果您创建一个if
块来运行某些代码 ifyear
不是 a number
,则它可能如下所示:
if (typeof year !== "number") {
year;
}
该块year
内变量的类型if
将是never
. 这是因为,由于year
类型为number
,因此if
永远不会满足此块的条件。您可以将该never
类型视为不可能的类型,因为此时该变量不能有值。
object
的object
类型表示的任何类型的不是原始类型。这意味着它不是以下类型之一:
number
string
boolean
bigint
symbol
null
undefined
该object
类型通常用于描述对象字面量,因为可以为其分配任何对象字面量:
const programmingLanguage: object = {
name: "TypeScript"
};
注意:有一种比object
在这种情况下可以使用的更好的类型,称为Record
. 这与创建自定义类型有关,将在本系列的后续教程中进行介绍。
结论
在本教程中,您尝试了 TypeScript 中可用的不同基本类型。在 TypeScript 代码库中工作时,这些类型将经常使用,并且是创建更复杂的自定义类型的主要构建块。
有关 TypeScript 的更多教程,请查看我们的如何在 TypeScript 中编码系列页面。