如何在TypeScript中使用基本类型
作者选择了 COVID-19 Relief Fund 作为 Write for DOnations 计划的一部分来接受捐赠。
介绍
TypeScript 是 JavaScript 语言的扩展,它使用带有编译时类型检查器的 JavaScript 运行时。 这种组合允许开发人员使用完整的 JavaScript 生态系统和语言功能,同时还添加可选的静态类型检查、枚举数据类型、类和接口。 这些特性为开发人员提供了 JavaScript 动态特性的灵活性,但也允许更可靠的代码库,其中类型信息可以在编译时用于检测可能导致错误或运行时其他意外行为的可能问题。
额外的类型信息还提供了更好的代码库文档,并在文本编辑器中改进了 IntelliSense(代码完成、参数信息和类似的内容辅助功能)。 队友可以准确地确定任何变量或函数参数的预期类型,而无需通过实现本身。
本教程将介绍类型声明和 TypeScript 中使用的所有基本类型。 它将引导您完成具有不同代码示例的示例,您可以在自己的 TypeScript 环境或 TypeScript Playground(一个允许您直接在浏览器中编写 TypeScript 的在线环境)中进行操作。
先决条件
要遵循本教程,您将需要:
- 一个环境,您可以在其中执行 TypeScript 程序以跟随示例。 要在本地计算机上进行设置,您将需要以下内容。 为了运行处理 TypeScript 相关包的开发环境,同时安装了 Node 和 npm(或 yarn)。 本教程使用 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),这也适用。 此外,您需要在您的机器上安装 TypeScript 编译器 (tsc)。 为此,请参阅官方 TypeScript 网站。
- 如果您不想在本地机器上创建 TypeScript 环境,您可以使用官方的 TypeScript Playground 进行操作。
- 您将需要足够的 JavaScript 知识,尤其是 ES6+ 语法,例如 解构、剩余运算符 和 导入/导出 。 如果您需要有关这些主题的更多信息,建议阅读我们的 如何在 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
类型 bigint
是针对 ES2020 时可以使用的类型。 它用于表示 BigInt,这是一种新的数据类型,用于存储大于 2^53
的整数。
试试下面的代码:
const bigNumber: bigint = 9007199254740993n;
注意: 如果此代码抛出错误,可能是 TypeScript 未设置为目标 ES2020
。 这可以在您的 tsconfig.json 文件 中更改。
如果您使用大于 2^53
的数字或使用某些数学库,则 bigint
将是一个常见的类型声明。
symbol
symbol
类型用于表示 Symbol 原始值。 这将创建一个唯一的、未命名的值。
使用 Symbol()
构造函数运行以下代码:
const mySymbol: symbol = Symbol('unique-symbol-value');
这些值的唯一性可用于避免引用冲突。 有关 JavaScript 中符号的更多信息,请阅读 Mozilla Developer Network (MDN) 上的 符号文章。
数组
在 TypeScript 中,arrays 是根据它们预期具有的元素进行类型化的。 输入数组有两种方法:
- 将
[]
附加到数组元素的预期类型。 例如,如果你想输入一个包含多个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]
格式存储 2D 坐标。 如果您正在使用 React 并使用 Hooks,大多数 Hooks 的结果也是一个元组,例如 const [isValid, setIsValid] = React.useState(false)
。
要键入一个元组,而不是键入一个数组,您需要将元素的类型包装在 []
中,并用逗号分隔它们。 想象一下,您正在创建一个包含元素类型的文字数组:
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
的特殊类型,这意味着任何类型。 使用 any
意味着选择退出类型检查,与让 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
类型。 当使用 unknown
类型的值执行任何操作时,TypeScript 需要确保类型是它期望的类型。 这样做的一个例子是使用 JavaScript 中已经存在的 typeof
运算符。 检查以下代码块:
if (typeof code === 'number') { const isCodeGreaterThan100 = code > 60; // ... } else { throw new Error('Invalid value received as code'); }
在此示例中,您使用 typeof
运算符检查 code
是否为数字。 当您这样做时,TypeScript 将 强制 将变量的类型转换为 if
块内的 number
,因为在运行时 if
中的代码] 块仅在 code
当前设置为数字时才会执行。 否则,您将抛出一个 JavaScript 错误,指出传递的值无效。
要了解 unknown
和 any
类型之间的区别,可以将 unknown
视为“我不知道该值的类型”和 any
作为“我不在乎这个值是什么类型”。
void
您可以使用 void
类型将相关变量定义为根本不包含任何类型。 如果将不返回值的函数的结果分配给变量,则该变量将具有 void
类型。
采取以下代码:
function doSomething() {}; const resultOfVoidFunction: void = doSomething();
您很少需要直接在 TypeScript 中使用 void
类型。
null
和 undefined
TypeScript 中的 null
和 undefined
值具有它们自己的唯一类型,它们以相同的名称调用:
const someNullField: null = null; const someUndefinedField: undefined = undefined;
这些在创建您自己的自定义类型时特别有用,这将在本系列的后面部分介绍。
never
never
类型是永远不会存在的值的类型。 例如,假设您创建了一个数值变量:
const year: number = 2021;
如果在 year
不是 number
的情况下创建 if
块来运行某些代码,则可能如下所示:
if (typeof year !== "number") { year; }
if
块内的变量 year
的类型将是 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 的更多教程,请查看我们的 How To Code in TypeScript 系列页面。