神奇的TypeScript
文章目录
前言
什么是TypeScript
,她为什么神奇呢?我们再来一次,这一次我们好好来~
什么是 TypeScript?
简单来说,TypeScript
就是给 JavaScript
添加了类型,告诉编译器这个变量应该是什么类型的数据。
再深入一点呢: TypeScript
是 JavaScript
的超集 ,即TypeScript
提供了 JavaScript
的所有功能,并在这些功能之上添加了一层: 可选的静态类型系统。意味着你现有的运行良好的 JavaScript
代码也是 TypeScript
代码。
为什么学习 TypeScript?
- 更安全的代码:静态类型可以在开发过程中就避免很多运行时错误。
- 更易维护的代码:类型信息可以更好地理解代码的逻辑。
- 更强的工具支持:
TypeScript
有更强大的工具支持,如代码补全、错误提示、代码导航等。 - 更常用的语言:
TypeScript
在现代前端开发中越来越重要,基本上所有的大厂大型项目都在使用她。
一、TypeScript基础与环境
1. 什么是静态类型检查
对于程序员来讲,静态类型检查就像一个严谨的老师,在你写代码的时候,就仔细检查你写的每个变量
、函数
、参数
等等,确保它们都符合你设定的规则
。
1.1 静态类型检查的特点
- 检查时间: 在代码运行之前进行检查。
- 检查对象: 检查代码中的类型信息,例如
变量的类型
、函数参数的类型
、返回值的类型
等等。- 目的: 确保代码在运行之前符合预期的类型规范,尽早发现潜在的错误,提高代码的可靠性和可维护性。
1.2 静态类型检查的优点
提前发现错误: 在编译阶段发现
类型错误
,避免运行时出现不可预期的错误,提高代码的可靠性
。
提高代码可读性: 类型信息可以帮助开发者更容易理解代码的逻辑,提高代码的可维护性
。
更强大的工具支持: 支持代码补全、自动类型推断、错误提示等功能,提高开发效率
。
1.3 静态类型检查的缺点
增加代码编写工作量: 需要为
变量
、函数
、参数
等添加类型信息,会增加代码编写的工作量。
灵活性降低: 在某些情况下,静态类型检查可能会限制代码的灵活性,例如需要使用 any 类型来绕过类型检查 (但不推荐使用any,会失去使用TypeScript的意义)。
// JavaScript 代码
const name = "张三";
const age = 20;
const user = {
name: age, // 这里会把年龄当作姓名赋值,在运行时会报错
};
// TypeScript 代码
const name: string = "张三";
const age: number = 20;
const user: { name: string; age: number; } = {
name: age, // 这里类型检查时就会报错,因为类型不匹配 Error: Type 'number' is not assignable to type 'string'.
};
2. 类型工具
TypeScript
所有的功能都建立在类型检查器上,并且是跨平台的,因此在使用时最好先在你使用的编辑器上准备好对应的工具。
TypeScript官方工具推荐
如果你不想这么麻烦的话,嘿嘿~~ 开箱即用:
- Visual Studio Code内置对
TypeScript
的支持。 - WebStorm以及其他 JetBrains IDE 都包含开箱即用的
TypeScript
语言支持。
3. TypeScript 编译器 —— tsc
3.1 什么是 tsc?
tsc
是 TypeScript
编译器的命令行工具,主要作用是将 TypeScript
代码编译成 JavaScript
代码。
简单来说,tsc
就相当于一个翻译官,能把我们写的 TypeScript
代码翻译成浏览器或 Node.js
能看懂的 JavaScript
代码。
3.2 tsc 的作用:
类型检查: tsc
会在编译过程
中对TypeScript
代码进行类型检查,确保代码符合类型规范,并尽早发现潜在的错误。
代码编译: tsc
会将您的 TypeScript 代码编译成 JavaScript 代码,以便在浏览器或 Node.js 中运行。
生成声明文件: tsc
可以生成声明文件 (.d.ts),用于描述 TypeScript 代码的类型信息,方便其他 TypeScript 代码引用您的代码。
配置编译选项: tsc
支持多种编译选项,例如设置输出目录、指定目标 JavaScript 版本、开启或关闭类型检查等。
3.2.1 了解一下:什么是编译时?什么是运行时?
3.2.1.1 基本概念
一句话解释: 编译过程是将源代码转换为低级的机器代码或虚拟机字节码
。 运行时是计算机执行机器语言代码,完成程序操作的过程
。
编译过程 就像把一本外语书翻译成你看得懂的语言,只不过这里的你指的是计算机或运行环境。
第一步: 将你写的代码(通常是高级语言,例如 TypeScript、Java、C++ 等)翻译成计算机可以直接理解的机器语言(通常是二进制代码)。
第二步: 将翻译好的机器语言保存成可执行文件。
运行时 就像你阅读翻译好的书籍一样,这里的你指的也是计算机或运行环境。
第一步: 计算机加载可执行文件,并开始执行里面的指令。
第二步: 计算机按照指令一步步执行,完成你想要的操作。
3.2.1.2 TypeScript 的编译过程和运行时
编译过程:
使用 tsc
编译器将 TypeScript
代码编译成 JavaScript
代码。
tsc
会检查代码中的类型错误,并生成 .js 文件
。
运行时:
浏览器或 Node.js
会加载和执行生成的 JavaScript
代码。
3.3 如何使用 tsc
3.3.1 安装
安装 Node.js
,再使用 npm
安装 tsc
npm install -g typescript
3.3.2 编译代码
在命令行中进入要编译的TypeScript
文件所在的目录,然后执行以下命令进行编译:
tsc fileName.ts
tsc
会将 fileName.ts
文件编译成 fileName.js
文件,并在同级目录下生成。
3.3.3 两种方式配置编译选项
3.3.3.1 命令行中添加参数
tsc
支持多种编译选项,具体可以使用 tsc --help
命令查看所有选项。
eg: 将 TypeScript 代码编译成 ES5 版本的 JavaScript 代码
tsc --target es5 fileName.ts
3.3.3.2 tsconfig.json配置
使用 tsc --init
命令可以生成一个默认的 tsconfig.json
文件,当然也可以在同级目录下创建一个名为 tsconfig.json
的配置文件
以下是 tsconfig.json 文件中常见的配置项:
{
"compilerOptions": {
/* 基本选项 */
"target": "es5", // 指定 JavaScript 版本,例如 'es5'、'es6'、'esnext'
"module": "commonjs", // 指定模块系统,例如 'commonjs'、'amd'、'system'、'es2015'
"moduleResolution": "node", // 指定模块解析策略,例如 'node'、'classic'
"outDir": "dist", // 指定输出目录,用于存放编译后的 JavaScript 文件
"sourceMap": true, // 是否生成 source map 文件,用于调试
"inlineSources": true, // 是否将源代码嵌入 source map 文件中
"declaration": true, // 是否生成声明文件 (.d.ts),用于描述 TypeScript 代码的类型信息
/* 类型检查选项 */
"strict": true, // 开启严格模式,启用更多的类型检查规则
"noImplicitAny": true, // 禁止隐式 `any` 类型,要求所有变量必须显式声明类型
"noImplicitThis": true, // 禁止隐式 `this` 类型,要求 `this` 必须明确定义
"noUnusedLocals": true, // 禁止未使用变量
"noUnusedParameters": true, // 禁止未使用函数参数
"strictNullChecks": true, // 开启严格的空检查,要求变量必须显式判断是否为 null 或 undefined
/* 代码生成选项 */
"removeComments": true, // 是否移除注释
"preserveConstEnums": true, // 是否保留常量枚举
"emitDecoratorMetadata": true, // 是否为装饰器生成元数据
"experimentalDecorators": true, // 是否启用实验性装饰器
/* 其他选项 */
"baseUrl": "./src", // 指定模块解析的基准目录
"paths": {
"@src/*": ["src/*"] // 配置模块别名,方便模块引用
},
"lib": ["dom", "es5", "es2015"], // 指定要包含的库文件,例如 DOM、ES5、ES2015
"types": ["node", "jquery"], // 指定要包含的第三方类型定义文件,例如 Node.js、jQuery
},
"include": [
"src/**/*" // 指定要编译的源代码文件
],
"exclude": [
"node_modules", // 指定要排除的目录
"dist"
]
}
3.3.4 如何钻类型检查空子,禁用规则或忽略特定错误(ps:不建议使用)
- 使用 // @ts-ignore 注释
将// @ts-ignore
注释放在需要忽略类型检查的代码行之前,只能禁止下一行不使用类型检查
// @ts-ignore
const myVar = "hello"; // 忽略本行的类型检查
- 禁用规则
在tsconfig.json
文件中禁用特定的规则。表示将禁用该规则在整个项目中应用
{
"compilerOptions": {
// ...
"noImplicitAny": false // 禁用 noImplicitAny 规则
}
}
- 禁用或启用 ESLint 规则
还可以使用/* eslint-disable */
和/* eslint-enable */
注释来禁用或启用 ESLint 规则
/* eslint-disable */
// 在此代码块中,所有 ESLint 规则都将被禁用
/* eslint-disable no-unused-vars */
// 在此代码块中,no-unused-vars 规则将被禁用
还可以使用逗号分隔多个规则名称:
/* eslint-disable no-unused-vars, no-undef */
注意:
总的来说,
/* eslint-disable */
和@ts-ignore
等禁用规则是一个强大的工具,可以帮助解决 ESLint 问题。但是,请务必谨慎使用,并尽可能保持代码的质量和一致性。
二、TypeScript 类型
1. 基本类型 (Primitive Types)
number
: 数字类型,包括整数和小数。
string
: 字符串类型,用来表示文本。
boolean
: 布尔类型,用来表示真假值,只有 true
和 false
两种值。
null
: 空值类型,表示一个值为空。
undefined
: 未定义类型,表示一个变量尚未赋值。
void
: 空类型,表示一个函数没有返回值。
never
: 永不返回值类型,表示一个函数永远不会返回。
unknown
: 未知类型,表示一个变量的类型未知。
any
: 任何类型,表示一个变量可以是任何类型。
2. 对象类型
2.1 object:表示任何对象,所有非原始类型的值
因此它是一个非常宽泛的类型,无法提供对象的具体结构信息,类似于非原始类型的any
。
在实际开发中尽量不去使用,通常建议使用更具体的类型,如接口或类,以提高代码的可读性和类型安全性。
// 这些赋值都是有效的,因为它们都是非原始类型
let obj1: object = { name: "Alice", age: 30 };
let obj2: object = [1, 2, 3];
let obj3: object = new Date();
2.2 接口 (interface):用于定义对象的结构,指定对象具有的属性和方法,以及它们的类型
interface Person {
name: string;
age: number;
}
2.2.1 type 和 interface的区别
通常情况下:
- 对于定义简单类型别名,使用
type
,需要定义类型别名
或使用联合类型
、交叉类型
等特殊类型时可以使用type
; - 对于定义复杂对象类型,使用
interface
,并且能够利用声明合并等功能,在大多数情况下,优先使用interface
。
1. 定义方式
type
使用 type
关键字,并使用等号 =
来定义类型别名。
type MyNumber = number;
type User = { name: string; age: number };
interface
使用 interface
关键字,并使用花括号 {}
来定义接口。
interface Person {
name: string;
age: number;
sayHello(): string;
}
2. 最大区别:声明合并
type
是封闭的,一旦定义就不能修改,也就不支持类型合并
interface
可以多次声明同一个接口,TypeScript会自动合并同名的interface
声明,也就是类型合并
// 这里两个A都会报错:Duplicate identifier 'A'. (标识符“A”重复)
type A = {};
type A = {};
interface B {
name: string;
};
interface B {
age: number;
};
// TypeScript会自动合并同名的声明
let bob: B = {
name: "",
age: 0
}
2.3 类 (class): 用于定义对象的蓝图,可以包含属性、方法和构造函数
class Dog {
name: string;
constructor(name: string) {
this.name = name;
}
bark(): string {
return "Woof!";
}
}
2.4 数组类型 (Array Types):表示一组相同类型的元素
可以使用两种方式定义:
// 使用方括号语法
let numbers: number[] = [1, 2, 3];
// 使用泛型
let strings: Array<string> = ["a", "b", "c"];
number[]
: 数字数组类型,例如:[1, 2, 3].
string[]
: 字符串数组类型,例如:[“hello”, “world”].
boolean[]
: 布尔数组类型,例如:[true, false].
Array<T>
: 泛型数组类型,T 代表任意类型。
PS:泛型我们后面再详细讲,先提一嘴,泛型是
TypeScript
中一个强大的特性,它允许你在编写代码时使用类型参数,而不是具体的类型,简单来讲就是给类型传参。
2.5 元组类型 (Tuple Types):元组类型允许你定义一个固定长度的数组,并且每个元素的类型都已知
例如:[string, number] 表示包含一个字符串和一个数字的元组。
let myTuple: [string, number] = ["Hello", 10];
2.4 枚举类型 (Enums):用于定义一组有名字的常量
enum Status {
Pending,
Approved,
Rejected
}
3. 联合类型 (Union)
联合类型允许一个变量接受多种类型的值。例如:
let value: number | string = 10;
value = "Hello";
4. 交叉类型 (Intersection)
交叉类型用于创建具有多个类型的所有属性的新类型。例如:
interface Bird {
fly: () => void;
}
interface Fish {
swim: () => void;
}
type FlyingFish = Bird & Fish;
5. 类型别名
类型别名用于为现有类型创建一个新的名字。例如:
type MyNumber = number;
let value: MyNumber = 10;
6. 泛型
什么是泛型:泛型就像一个类型占位符,允许你在编写代码时不指定具体的类型,而是将其推迟到使用的时候再确定。也就是我们上面说的它允许你在编写代码时使用一个类型占位符,而不是具体的类型。简单来讲就是给类型传参
。
泛型的使用场景:
函数: 编写可以处理多种类型的函数。
// 泛型函数,用于交换两个变量的值
function swap<T>(a: T, b: T): [T, T] {
return [b, a];
}
//交换两个数字
let num1 = 10;
let num2 = 20;
[num1, num2] = swap(num1, num2);
console.log(`交换后的数字:num1 = ${num1}, num2 = ${num2}`);
// 交换两个字符串
let str1 = "hello";
let str2 = "world";
[str1, str2] = swap(str1, str2);
console.log(`交换后的字符串:str1 = ${str1}, str2 = ${str2}`);
类: 定义可以处理多种类型的类。
// 泛型类,用于存储任意类型的值
class DataContainer<T> {
private data: T;
constructor(data: T) {
this.data = data;
}
getValue(): T {
return this.data;
}
}
// 存储数字
let numberContainer = new DataContainer<number>(100);
console.log(`数字容器中的值:${numberContainer.getValue()}`);
// 存储字符串
let stringContainer = new DataContainer<string>("TypeScript");
console.log(`字符串容器中的值:${stringContainer.getValue()}`);
接口(interface): 定义可以处理多种类型的接口。
// 泛型接口,定义一个具有泛型属性的类型
interface KeyValuePair<K, V> {
key: K;
value: V;
}
// 创建键值对
let keyValuePair: KeyValuePair<string, number> = {
key: "age",
value: 30
};
console.log(`键值对:key = ${keyValuePair.key}, value = ${keyValuePair.value}`);
总结
以上就是今天的全部内容啦,我们通过TypeScript
编译器tsc的使用方法,包括安装、编译代码、配置编译选项,以及如何禁用规则或忽略特定错误。接着,深入了解了一下TypeScript
的类型系统,涵盖了基本类型、对象类型、接口、类、数组、元组、枚举,以及更高级的类型特性如联合类型、交叉类型、类型别名和泛型。也希望为大家提供了一个全面的TypeScript
学习指南。通过这些希望大家可以对TypeScript
有一个系统的认识。熟悉以上内容之后,下一节我们再详细了解一下TypeScript
的一些代码机制。