TypeScript 入门 - 基础知识
为什么(不)?
基础知识
更多类型!
成分类型
函数
更多功能
有很多内容需要涵盖
这篇文章摘自我的博客,记得去看看博客上的最新内容哦😉
如果你读过我之前的文章,或者任何关于 JavaScript 的博客,那么你很可能听说过TypeScript。对于那些不了解TypeScript 的朋友——TS(TypeScript 的官方缩写)其实就是添加了静态类型系统的现代 JavaScript。它最近备受关注,这主要是因为它比标准的动态类型 JavaScript 更具优势。所以,在本文中,我将指导你如何入门 TypeScript,前提是你已经掌握了 JavaScript。这将是一个深入的系列,涵盖从基础知识到一些复杂技巧的几乎所有内容。我会尽量用最简单的方式解释(各位大神们,请不要指责我过度简化🙃)。祝你学习愉快! 😉
为什么(不)?
在学习 TS 之前,让我们先来探讨一下为什么它值得你付出努力。
首先,TypeScript 是一种编译型语言,其默认编译目标是 JavaScript。它是一个由微软发起并维护的开源项目。它内置支持现代ES-Next 特性和静态类型系统。虽然很多日常 JavaScript 开发者都使用 Babel 等工具来利用 ES-Next,但静态类型的概念对你来说可能比较陌生(除非你之前接触过其他静态类型语言😁)。
静态类型在编译时就确定了变量的类型。JavaScript 是一种解释型或即时编译型语言,它采用动态类型系统。但更重要的是,对于像你这样的编程语言最终用户(也就是消费者)来说,它在实际应用中的表现如何。在这种情况下,静态类型可以降低出错率,通常也能带来更完善的IDE 支持和工具。因此,它可以极大地改善你的编码体验。至于动态类型系统,它也有自己的优势。最主要的一点是,你不需要在代码中直接指定类型。😅
除了前面提到的静态类型系统之外,并没有太多其他能带来显著差异的原因。所以,如果你想学习 TypeScript,提升你的知识和开发经验,那就跟着我一起探索静态类型的潜在价值吧。
基础知识
TypeScript 提供的每个功能都是非侵入式的,这意味着它的语法不会与 JavaScript 代码的任何部分重叠。这使得 JavaScript 应用在 TypeScript 和 JavaScript 之间移植变得相对容易。
您可以使用冒号 ( :) 后跟类型名称来指定变量的类型:
const myStringVariable: string = "str";
需要记住的六种基本原始类型:
- 数字- 表示任何类型的数值- 整数或浮点数、十六进制、十进制、二进制等。
- 字符串- 表示任何类型的字符串值;
- boolean - 表示任何布尔值,即 true
true或falsefalse; - 符号- 表示
symbol值; - null仅表示
null值; - 未定义- 仅表示
undefined值;
这应该不是什么新鲜事。以上每种类型在 JavaScript 语言本身都有详细的文档说明。只是由于 JavaScript 的动态类型系统,这一点才显得不那么显眼。但请放心,TypeScript 的功能远不止这些。我觉得我们应该深入探索一下!🌟
更多类型!
目的
首先,我们来看看更复杂的类型,比如 `int` 类型object。需要记住的是,它代表任何非原始值。这意味着原始值是不可赋值的。这是因为在 JavaScript 中,几乎所有东西都是对象。正如你所看到的,TypeScript 非常尊重 JavaScript 的架构。😀
const myObjectVariable: object = "str"; // error
const myObjectVariable2: object = {};
任何
Any顾名思义,`type` 表示任何可能的值。它就像一种备用方案,允许你省略类型检查。在从 JavaScript 移植到 TypeScript 的初期,它非常有用。但是,它不应该被过度使用,最好是完全不要使用!你用 TypeScript 可不是为了: any到处乱写类型,对吧?😂
let myAnyVariable: any = "str";
myAnyVariable = 10;
myAnyVariable = true;
空白
Void类型,顾名思义,代表完全没有类型。它通常用于函数,告诉编译器该函数不返回任何值。这里的“任何值”包括 `int`undefined和 `int` null,但谁在乎呢?反正它们看起来都像是无效的。😅 顺便一提,你很可能不会在变量中使用这种类型,但你可以体验一下它带来的奇怪感觉:
let myVoidVariable: void = undefined;
myVoidVariable = null;
绝不
Never根据纯粹的定义,`type` 代表永远不会出现的值。但它究竟是什么意思呢?基本上,它指的是例如抛出/返回错误的函数的返回类型,这种类型不允许函数拥有可到达的终点。它也与所谓的类型守卫(稍后会详细介绍)一起使用。一般来说,这种类型并不常用。无论如何,稍后会提供示例以及一些更高级的内容。
未知
Unknown 是 TypeScript 类型集合中相对较新的成员,它在v3.0 版本中引入。它的目的是作为anytype 的一种类型安全的替代方案。它是如何工作的呢?首先,任何值都可以赋给 unknown,就像 type 一样any:
const myUnknownVariable: unknown = "str";
const myAnyVariable: any = "str";
当把某个unknown类型的变量赋值给其他任何类型时,区别就显现出来了。我的意思是:
let myUnknownVariable2: unknown = myUnknownVariable;
myUnknownVariable2 = myAnyVariable;
let myStringVariable: string = "str";
myStringVariable = myAnyVariable;
myStringVariable = myUnknownVariable; // error
未知数只能分配给它自身……
这是官方 TS 文档中的说法,也是对“任何”和“未知”之间区别的一般性解释。
成分类型
到那时,我们已经了解了TypeScript 的基本类型和顶级类型(也就是上面章节中提到的那些内置类型)。现在是时候探索一些更有趣的类型了。这些类型并不总是能在 JavaScript 中找到直接对应的类型。我称它们为组合类型,因为它们是由一些更小的部分组成的。需要说明的是,这个名称并非官方名称。😁
工会
简而言之,联合体允许你指定变量的类型,并为其赋值不同类型的值。它们就像一个可赋值的类型列表。你可以用竖线符号 ( ) 分隔类型来指定它们|。
let myUnionVariable: string | number = "str";
myUnionVariable = 10;
myUnionVariable = false; // error
联合类型拥有巨大的潜力。例如,你可以使用它们来处理函数中不同类型的参数,或者any用这些真正类型安全的替代方案来替换你现有的类型。
字面意义
字面量类型允许你严格定义变量的可能值。字面量本身并非任何组合类型,但由于它们经常与联合类型等一起使用,所以我将它们归入此类。那么,字面量类型长什么样呢?嗯,就像它所注解的可能值一样:
let myStringLiteral: "str" = "str";
let myNumberLiteral: 10 = 10;
let myBooleanLiteral: true = true;
myStringLiteral = "string"; // error
myNumberLiteral = 1; // error
myBooleanLiteral = false // error
我认为通过上面的例子,您可以轻松理解字面类型背后的概念,并且可以想象它们与联合类型等类型的集成效果如何:
let myVariable: "on" | "off" = "off";
myVariable = "on";
myVariable = "suspend" // error
但如果你想用字面意义(这里用“字面意义”这个词很贴切🙃)来表达一些更复杂的值,比如一个对象呢?其实,你只需要做同样的事情:
let myObjectLiteral: {str: string} = {str: "str"};
myObjectLiteral.str = "string";
myObrjectLiteral.num = 10; // error
交叉类型
交集类型与并集类型密切相关。并集类型类似于逻辑“或”,而交集类型类似于逻辑“与”。因此,您可以使用“与”符号 ( &) 创建它们。
const myIntersectionVariable: {str: string} & {num: number} = {
str : "str",
num: 10
};
创建的类型具有所有操作数的属性。这些类型通常与对象字面量和其他复杂类型及技术一起使用,我们将在后面介绍。
数组
了解了这么多类型之后,是时候认识一下我们熟悉的数组了。这里,我将介绍第一种输入数组值的方式。因为实现同一个目标有两种方法——稍后会详细介绍。现在,要表示数组类型,你需要写出数组实际值的类型,并在其前面加上方括号符号 ( [])。
const myStringArrayVariable: string[] = ["str", "str"];
提醒一下——你可以将之前介绍过的许多类型组合起来使用。例如,你可以使用联合类型创建一个字符串和数字数组的类型,或者创建一个字面值数组的类型。选择无穷无尽!🤯
const myUnionArrayVariable: (string | number)[] = ["str", 10];
const myLiteralArrayVariable: ("str")[] = ["str","str"];
我想你到那时应该已经知道,在 TypeScript 中,额外的空格无关紧要。另外,请注意()上面代码片段中的圆括号 ( )。就像在普通数学(以及 JavaScript)中一样,它们用于将内容组合在一起。这似乎很合乎逻辑。😅
元组
与数组密切相关的结构体,即所谓的元组,可以用来指定一个具有固定数量元素的数组类型,并且每个元素都具有严格指定的类型。请看以下示例:
const myTupleVariable: [number, string] = [10, "str"];
const myTupleVariable2: [string, number] = [10, "str"]; // error
它几乎解释了所有内容。要定义元组类型,首先要用方括号 ( []),这是所有数组的典型特征,然后逐个包含元组的类型,并用逗号分隔。总之,这非常符合逻辑。
枚举
对于一些 JavaScript 程序员来说,枚举可能感觉比较陌生。但实际上,枚举在静态编程语言社区中早已广为人知。它们的作用很简单,就是为数值提供更友好的名称。例如,在配置对象中,我们经常需要使用不同的数字。这就是枚举的用武之地。
枚举类型的定义方式与我们之前遇到的任何类型都略有不同。具体来说,它是通过enum关键字定义的。
enum Color {Red, Green, Blue};
在上面的例子中,我们定义了一个名为 `enum` 的枚举,Color它包含三个成员:Red`a`、Green`b` 和 `c` Blue。默认情况下,每个成员的编号都从 0 开始,每增加一个成员,编号就递增 1。也就是说,使用枚举,你可以同时访问成员的数值和名称!让我来演示一下。😁
Color.Red // 0
Color.Blue // 2
Color[1] // "Green"
Color[2] // "Blue"
如你所见,枚举类型可以轻松地用于标准值。但我们是在 TypeScript 中,这里讨论的是类型,那么如何将枚举类型用作类型呢?很简单——就像使用其他类型一样!
let myColorEnumVariable: Color = Color.Red;
myColorEnumVariable = 2;
myColorEnumVariable = Color[1]; // error
所以,枚举类型的变量实际上可以看作是数字字面量的并集,我认为。你可以给它赋值一个合适的数字或枚举成员的值。不允许赋值给其他任何值,即使是成员名称也不行。
现在,简单提一下枚举成员的编号。正如我所说,默认情况下,编号从 0 开始,每增加一个成员,编号就递增 1。但实际上,您可以通过直接赋值来更改此设置。
enum Color {Red, Green = 32, Blue};
Color.Red // 0
Color.Green // 32
Color.Blue // 33
在上面的例子中,我们覆盖了Green成员的值。这样,该Red值保持不变——默认值为 0,Green被赋值为 32,而Blue由于递增规则,其值为 33。
总而言之,枚举类型如果使用得当非常有用,但恕我直言,它们的语法对于 JavaScript 用户来说也是最难记住的(或者说最新)的语法之一。不过,当我们讨论接口时,枚举类型会非常有用,所以我们继续吧!⚡
函数
学完以上所有类型和其他相关知识后,我觉得是时候学习如何正确地给函数添加类型了!有了这些知识,你应该就能真正开始编写一些 TypeScript 代码了!
编写函数与之前编写的其他 TypeScript 代码类似。我们仍然使用冒号和常见的类型名称语法,但位置有所不同。
function myFunction(myStringArg: string, myNumberArg: number): void
{
// code
}
如您所见,函数参数部分后面跟着我们标准的类型注解。它告诉编译器函数的返回值类型。在上面的例子中,它是 `null` void。我之前在讨论这种特殊类型时提到过,它实际上表示没有任何类型。这意味着我们上面的函数不返回任何值。很简单,对吧?
当然,编写函数远不止上面这段代码所能展示的。如果我们想编写函数表达式呢?由于箭头函数的流行,这种情况最近非常普遍。那么,该如何编写函数表达式呢?
const myFunctionExpression: (arg: string, arg2: number) => void =
(arg, arg2) => {
// code
}
上面你可以看到函数类型的大致样子。它看起来和标准的箭头函数很相似,不是吗?
(arg: string, arg2: number) => void
我们通过函数表达式为变量赋值,其中参数没有指定类型。这是因为我们已经为函数指定了类型,所以无需重复。
函数类型和其他任何类型一样,也可以用作另一个函数的参数类型。
function myFunction(funcArg: () => void): void {
// code
}
这里我接受一个不接受任何参数也不返回任何值的函数作为参数。再次提醒,这些函数可以轻松地与其他 TypeScript 类型混合使用。😉
但是,如果你想添加一个额外的、非必需的参数呢?如何表示某个参数是可选的?很简单——在参数名称前加上问号(?)!
function myFunction(myArg: number, myOptionalArg?: string): void {
// code
}
你可以拥有多个可选参数。但是,显而易见,可选参数后面不能跟任何类型的必需参数。定义可选参数还有一种更长的语法,你考虑过吗?
function myFunction(myArg: number, myOptionalArg: string | undefined): void {
// code
}
是的,这个问号只是将你的类型与一个变量进行联合undefined。而且,由于可选类型的语法在其他一些地方也有使用,所以需要注意的是,它并非在所有地方都适用。在不适用的地方,你可以使用上面的语法,它总是有效的。😁
更多功能
读到这里(如果你认真阅读了这篇文章),你应该已经对一些TS类型有了不错的了解——包括一些基础的和更复杂的类型。但TS远不止这些!所以,让我们一起来探索一些能让你的TS生活更轻松的有趣内容吧!👍
类型推断
到目前为止,在之前的代码片段中,我们都是逐个定义类型。这似乎让我觉得静态类型语言需要编写更多的代码!别担心,朋友——并非如此!许多静态类型语言都具有所谓的类型推断,它允许编译器在无需任何特殊注解的情况下为特定变量选择正确的类型。因此,在变量声明时就赋值,或者在定义函数返回类型时,您可以放心地移除类型注解,仍然能够享受到静态类型带来的所有好处。
const myStringVariable = "str"; // string
const myNumberVariable = 10; // number
const myObjectVariable = {
str: "str",
num: 10
}; // {str: string, num: number}
如您所见,类型推断使我们的代码看起来更简洁,整体性能也更好。
类型推断的工作原理是推断最佳通用类型。这意味着推断出的类型力求尽可能通用。因此,如果您只想允许严格定义的字面类型,仍然需要对其进行严格的注解。
类型保护
还记得联合体吗?我之前介绍过联合体,你有没有想过它是如何处理某些事情的?因为,你知道,当一个变量有类型时string,IDE 可以利用这个信息提供很多有用的功能,比如为 JavaScript类型方法提供正确的自动补全string。但是,当一个变量只有类型string | number时,IDE 只能显示这些类型共享的方法。更糟糕的是,你只能将这样的变量赋值给明确允许的地方string | number。但是,如果你想将类似这样的变量赋值给类型string或number单独赋值给其他类型呢?
仔细想想。首先,你必须确保你的变量(它可以有两种类型)恰好是所需的类型。如何实现这一点呢?使用所谓的类型守卫。类型守卫只不过是 TypeScript 中对 JavaScript 运算符(例如 `or` 和 ` typeofor`)的一个花哨称呼instanceof。在 TypeScript 中,它们并没有比 JavaScript 增加任何额外的功能,所以使用方法也和 JavaScript 一样。它们底层的工作原理是将变量的类型限制为某种特定类型——在我们的例子中是 `or` number。
const myUnionVariable: string | number = 10;
function myFunction(arg: number) {
// code
}
myFunction(myUnionVariable); // error
if( typeof myUnionVariable === "string" ){
myFunction(myUnionVariable);
}
您还可以通过检查值是否具有某个属性、是否等于某个值等方式轻松定义自己的类型守卫。此类类型守卫采用具有特定返回类型的函数的形式。
function isOne(num: number): num is 1 {
return num === 1;
}
const myNumberVariable: number = 1; // number
isOne(myNumberVariable) // 1
你的类型守卫是一个返回布尔值的函数。如果返回值为真,则参数类型为之前注解的类型。这个注解是在函数的返回类型中使用is关键字实现的,左侧是参数名称,右侧是函数返回时要转换的类型true。这种方法相当简单直接,但在处理复杂的结构和类型时却非常实用。
类型铸造
类型转换(也称为类型断言)是一种极其强大的技术,在许多(如果不是全部)静态类型语言中都有应用。当你比编译器更了解变量的类型时,它就非常有用。当你的编译目标是动态类型语言(例如 JavaScript)时,这种情况尤为常见。简而言之,它允许你通过“暴力”的方式无限制地更改变量的类型。👊 在 TypeScript 中,有两种不同的语法可以实现此功能。
const myAnyVariable: any = "str";
const myStringVariable: string = <string>myAnyVariable;
const myStringVariable2: string = myAnyVariable as string;
你可以用尖括号 ( ) 将要转换的变量括起来,<>并在尖括号内指定目标类型;或者使用as关键字 `type`,后跟目标类型。这两种方法没有区别,你可以选择最适合你的方式。
在上面的例子中,我将类型为`T` 的变量强制转换any为 `T` string,但如果你不在any代码中使用类型转换(强烈建议使用),你可能根本不会遇到这种情况。然而,信不信由你,类型转换的用途远不止于此。只是要注意不要过度使用类型转换,因为它会在不知不觉中大幅降低代码的类型安全性。
有很多内容需要涵盖
如果你已经读到这里,那么恭喜你——你已经迈出了TypeScript和静态类型语言世界的第一步。但还有很多内容值得探索!所以,如果你喜欢这篇文章,请考虑分享 给其他对学习 TypeScript 感兴趣的人,并在下方点赞或留言,让我知道你是否想看到后续文章,我会讲解接口、泛型以及其他一些很棒的技巧等更高级的主题!此外,请在 Twitter和Facebook 上关注我,以便及时了解本系列文章以及更多关于 JavaScript 的精彩内容!
希望这篇文章能让你对TS有所了解,并鼓励你拓展知识面。再次抱歉,如果我没有解释得非常详细和技术性,因为这只是一篇友好的入门教程。😁 总之,感谢阅读,下次再见!🚀
文章来源:https://dev.to/areknawo/typescript-introduction---the-basics-3pmd