神奇的TypeScript -- 基础篇

神奇的TypeScript

TypeScript 官方文档


前言

什么是TypeScript,她为什么神奇呢?我们再来一次,这一次我们好好来~

什么是 TypeScript?
简单来说,TypeScript 就是给 JavaScript 添加了类型,告诉编译器这个变量应该是什么类型的数据。
再深入一点呢: TypeScriptJavaScript超集 ,即TypeScript 提供了 JavaScript 的所有功能,并在这些功能之上添加了一层: 可选静态类型系统。意味着你现有的运行良好的 JavaScript 代码也是 TypeScript 代码。

为什么学习 TypeScript?

  • 更安全的代码:静态类型可以在开发过程中就避免很多运行时错误。
  • 更易维护的代码:类型信息可以更好地理解代码的逻辑。
  • 更强的工具支持:TypeScript 有更强大的工具支持,如代码补全、错误提示、代码导航等。
  • 更常用的语言:TypeScript 在现代前端开发中越来越重要,基本上所有的大厂大型项目都在使用她。

一、TypeScript基础与环境

1. 什么是静态类型检查

对于程序员来讲,静态类型检查就像一个严谨的老师,在你写代码的时候,就仔细检查你写的每个变量函数参数等等,确保它们都符合你设定的规则

1.1 静态类型检查的特点

  • 检查时间: 在代码运行之前进行检查。
  • 检查对象: 检查代码中的类型信息,例如变量的类型函数参数的类型返回值的类型等等。
  • 目的: 确保代码在运行之前符合预期的类型规范,尽早发现潜在的错误,提高代码的可靠性和可维护性。

1.2 静态类型检查的优点

提前发现错误: 在编译阶段发现类型错误,避免运行时出现不可预期的错误,提高代码的可靠性
提高代码可读性: 类型信息可以帮助开发者更容易理解代码的逻辑,提高代码的可维护性
更强大的工具支持: 支持代码补全、自动类型推断、错误提示等功能,提高开发效率

1.3 静态类型检查的缺点

增加代码编写工作量: 需要为变量函数参数等添加类型信息,会增加代码编写的工作量。
灵活性降低: 在某些情况下,静态类型检查可能会限制代码的灵活性,例如需要使用 any 类型来绕过类型检查 (但不推荐使用any,会失去使用TypeScript的意义)

Examples

// 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?

tscTypeScript 编译器的命令行工具,主要作用是将 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:不建议使用)

  1. 使用 // @ts-ignore 注释
    // @ts-ignore 注释放在需要忽略类型检查的代码行之前,只能禁止下一行不使用类型检查
// @ts-ignore
const myVar = "hello"; // 忽略本行的类型检查
  1. 禁用规则
    tsconfig.json 文件中禁用特定的规则。表示将禁用该规则在整个项目中应用
 {
  "compilerOptions": {
    // ...
    "noImplicitAny": false // 禁用 noImplicitAny 规则
  }
}
  1. 禁用或启用 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: 布尔类型,用来表示真假值,只有 truefalse 两种值。
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的一些代码机制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小前端--可笑可笑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值