1.TS4类型系统之基础使用

书诚小驿2024/10/09前端知识库TypeScript

前言

TS 编写代码的好处诸多,下面我们列出几点: 友好的 IDE 提示 强制类型,防止报错 语言编写更加严谨 快速查找到拼写错误 JS 的超集,扩展新功能

一、TS 运行环境搭建

TypeScript 是微软开发的一个开源的编程语言,通过在 JavaScript 的基础上添加静态类型定义构建而成。TypeScript 通过 TypeScript 编译器或 Babel 转译为 JavaScript 代码,可运行在任何浏览器,任何操作系统。

1、TS 文件需要编译成 JS 文件

  • 新建ts-study项目,

  • 安装 ts 插件

首先我们的浏览器是不认识 TS 文件的,所以需要把 TS 编译成 JS 文件才可以,TS 官网提供了一种方式,就是去全局安装 typescript 这个模块,命令如下:

npm init -y
npm install -g typescript
  • 新建01_demo.ts
let test = 123;
console.log(test, "test");
  • 新建index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="./01_demo.js"></script>
  </head>
  <body></body>
</html>
  • 编译ts文件命令

默认自动编译对应的 js 文件

tsc 01_demo.ts

2、ts 文件自动编译 js 文件

tsc命令进行转换操作的时候,是不能实时进行转换的,那么可以通过添加一个-w的参数来完成实时转换的操作

tsc 01_demo.ts -w

3、ts 文件使用模块化变成局部环境

在编译后,我们会发现 TS 文件中定义的变量会产生错误的波浪线,这主要是因为 TS 默认是全局环境下的,所以跟其他文件中的同名变量冲突了,所以需要使用模块化操作来使其变成局部环境

let test = 123;
console.log(test, "test");
export {};

4、tsconfig.json文件

  • 生成tsconfig.json文件
tsc --init

"outDir": "./"换成"outDir": "./dist",自动将其打包到 dist 目录下 "include": ["01_demo.ts"]只转化 01_dmeo.ts 文件, 默认是`"include": ["*"]·将其所有 ts 文件转化

  • 默认将所有 ts 文件转化命令
ts
  • "module": "commonjs"

转化cjs模块风格,还可以选择ES6

  • "target": "es2016"

可以指定模块版本,如ES5

二、类型声明空间,变量声明空间

  • 类型注解

同时包含类型声明空间(type A =string)与变量声明空间(let a='hello')

let a:string=''hello world'
  • 类型声明空间(type A =string)
type A = string;
  • 变量声明空间(let a='hello')
let a = "hello world";
  • 还可以省略类型注解(如果 TypeScript 可以从初始值中推断出类型
let a = "hello world";
  • 类在 TS 中即是变量声明空间也是类型声明空间
class fn {}
let a = fn;
type A = fn;

三、类型分类与使用

类型归类类型
基本类型string number boolean null undefined symbol bigint
对象类型[] {} function()[]
TS 新增类型any never void unknown enum

1、基本类型

  • bigint

使用时 ES 不能低于 2020,任意精度的整数类型,用于表示大于 Number.MAX_SAFE_INTEGER(即 2^53 - 1)的整数。

let b : bigint =1n
  • symbol

符号类型,用于表示唯一的标识符,通常用于对象的属性键

// 创建一个 symbol 类型的值
let sym: symbol = Symbol("mySymbol");
// 创建一个对象,并使用 symbol 作为属性键
let obj: { [key: symbol]: string } = {};
obj[sym] = "Hello, symbol!";
// 尝试使用普通字符串作为键来访问该属性会失败
console.log(obj["mySymbol"]); // undefined,因为属性键是 symbol 类型,不是字符串
// 使用正确的 symbol 键来访问属性
console.log(obj[sym]); // 输出:"Hello, symbol!"

2、联合类型,(或|)

类型之间进行或的操作

let a: string | number = "hello";
a = "hello world";
a = 000;

3、交叉类型,(与&)

类型之间进行与的操作

type A = {
  username: string;
};
type B = {
  age: number;
};
let a: A & B = { username: "muying", age: 20 };

4、never 类型、any 类型、 unknown 类型

  • never 类型

never 类型在 TypeScript 中代表那些永不存在的值的类型。具体来说,它表示的是那些永远不会有返回值的函数(如抛出错误的函数或无限循环的函数)的返回类型。

function fn(n: 1 | 2 | 3) {
  switch (n) {
    case 1:
      break;
    case 2:
      break;
    case 3:
      break;
    default:
      // 检测n是否可以走到这里,看所有值全部被使用到
      let m: never = n;
      break;
  }
}
fn(1);
  • any 类型

当声明一个变量为 any 类型时,可以在这个变量上执行任何操作,而 TypeScript 编译器不会给出类型错误。这在一定程度上类似于 JavaScript 的动态类型系统,但在 TypeScript 中,any 类型是显式声明的。

let a: any = "hello";
a = 123;
  • unknown 类型

与 any 类型相似,但 unknown 更加安全,因为它不允许你在不知道其确切类型的情况下对值进行任何操作。在类型检查上,unknown 类型的值被当作是安全的,因为任何值都可以被赋值给 unknown 类型的变量。

let a: unknown = "hello";
a = 123;

5、类型断言

  • 类型断言

类型断言有两种形式:尖括号形式(<Type>value)和 as 关键字形式(value as Type

let a: unknown = "hello world";
(a as []).map(() => {});
  • 非空类型断言

使用 ! 后缀来明确地告诉 TypeScript 编译器某个值不是 null 或 undefined。

let a: string | undefined = undefined;
b!.length;

6、数组类型

  • 类型[]
let a: number[] = [1, 2, 3];
  • Array<类型>
let a: Array<number> = [1, 2, 3];
  • 混合数组类型
let arr: (number | string)[] = [1, 2, 3, "hello", "world"];
  • 元组类型

元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同,限定了个数,顺序也需要保持一致

let arr: [number: string] = [1, "hello"];

7、对象类型

  • 直接字面量

当你有一个具有确切属性名和类型的对象时,可以直接使用字面量形式定义其类型。

type Person = {
  name: string;
  age: number;
  greet(): void;
};

const john: Person = {
  name: "XiaoMu",
  age: 20,
  greet() {
    console.log("Hello World");
  },
};
  • 可选属性

可以通过在属性名后加上 ? 来标记属性为可选的。

type Person = {
  name: string;
  age?: number; // 可选属性
  greet(): void;
};
const jane: Person = {
  name: "XiaoMu",
  greet() {
    console.log("Hello World");
  },
  // age 属性在这里是可选的,所以可以省略
};
  • 只读属性

使用 readonly 关键字可以定义只读属性,这些属性在对象被创建后不能被修改。

type Person = {
  readonly id: number; // 只读属性
  name: string;
};
const person: Person = {
  id: 1,
  name: "XiaoMu",
};
// 下面这行代码将会引发错误,因为 id 是只读的,不能被修改
// person.id = 2;
  • 索引签名

如果不确定对象会有哪些属性,如果知道它们的类型,可以使用索引签名。

type Dictionary = {
  [key: string]: string; // 使用 string 类型的键和值的索引签名
};
const dict: Dictionary = {
  firstName: "XiaoMu",
  lastName: "XiaoBu",
};
// 索引签名允许你使用任何 string 类型的键来访问值
console.log(dict["firstName"]); // 输出: XiaoMu
  • 复杂类型写法
//type one
type Obj={username:string}
let obj={} as Obj
obj.username='nice'
//type two
let users :{username:string,age:number}[]=[]
// 可以向这个数组中添加符合 { username: string, age: number } 类型的对象
users .push({ username: 'XiaoMu', age: 20 });

8、函数类型与 void 类型

  • 函数类型使用
// TS要求:实参的个数跟形参的个数必须相同
function fn(count: number, m?: string) {
  console.log(count, "count");
}
fn(123);
fn(123, "hello world");

// // 如果()后在跟一个类型值,表示需要返回的类型值
// function fn1(count: number, m?: string): number {
//     console.log(count, 'count')
//     return count
// }

// fn1(20)

// // 函数表达式
// let test: (count: number, m?: string) =>number=function fn(m,n) {
//     console.log(m, 'count')
//     return 123
// }
  • void 类型

void 类型在 TypeScript 中通常表示没有返回值的函数。当看到函数的返回类型是 void 时,这意味着这个函数不返回任何值(或者更确切地说,它返回 undefined)。如果写的类型为 undefined,则不能不返回 reAturn,如果为 void,既可以返回 return 也可以不写

9、函数重载与可调用注解

  • 模拟函数重载

在 TypeScript 中,你可以使用联合类型来模拟函数重载。每个重载签名都是一个独立的函数签名,它们被组合成一个类型,该类型作为函数的实际类型。但是,你只能在函数体内部实现一个实现逻辑

function overloadFunc(name: string): string;
function overloadFunc(age: number): string;
function overloadFunc(nameOrAge: string | number): string {
  // 注意:这里只有一个实现
  if (typeof nameOrAge === "string") {
    return `Hello, ${nameOrAge}`;
  } else {
    return `Age: ${nameOrAge}`;
  }
}
// 使用函数
const greeting = overloadFunc("XiaoMu"); // 调用字符串版本的函数
const ageStatement = overloadFunc(20); // 调用数字版本的函数
  • 可调用注解

在 TypeScript 中,可以定义一个类型,该类型表示一个可调用的对象(即函数)。这通常是通过定义一个接口,并在该接口中声明一个带有特定签名的调用签名(call signature)来实现的。

type A = {
  username: string;
};
let fn: A = {
  username: "xiaomu",
};

定义了一个名为 A 的类型,该类型表示一个对象

type A={
 (n:number):number
 username?:string
}
let fn:A=(n)=>{return n}
fn.username='xiaomu'

函数重载与可调用注解相结合

type A = {
  (n: number, m: number): any;
  (n: string, m: string): any;
};
function fn(n: number, m: number): any;
function fn(n: string, m: string): any;
function fn(n: number | string, m: number | string): any {}
let a: A = fn;

通过定义一个接口,并在该接口中声明一个带有特定签名的调用签名(call signature)来实现的。

interface Callable {
  (arg: string): void;
}
// 实现这个接口的函数
const myFunction: Callable = (arg) => {
  console.log(arg);
};
// 调用函数
myFunction("Hello, world!");

10、枚举类型

  • 枚举(Enum)

枚举类型定义了一组命名的常量。默认情况下,枚举成员的值是递增的整数,从 0 开始。也可以为枚举成员指定任何值。

enum Color {
  Red,
  Green,
  Blue,
}
console.log(Color.Red); // 输出: 0
console.log(Color.Green); // 输出: 1
console.log(Color.Blue); // 输出: 2

为枚举成员指定特定的值

enum Color {
  Red = 1,
  Green,
  Blue = 4,
}
console.log(Color.Green); // 输出: 2,因为Green在Red之后,所以它的值是Red的值加1
console.log(Color); //未映射过的原有枚举

反向映射

enum Color {
  Red,
  Green,
  Blue,
}
console.log(Color[0]); // 输出: Red
console.log(Color[1]); // 输出: Green
console.log(Color[2]); // 输出: Blue
console.log(Color); //映射过的原有枚举

枚举既可以作为值也可以作为类型

enum Roles {
  SUPER_ADMIN = "super_admin ",
  ADMIN = "admin",
  USER = "user",
}
if (Roles.ADMIN == "admin") {
  console.log(Roles.ADMIN, "Roles.ADMIN");
}
  • const 枚举(Const Enum)

const 枚举与普通枚举的主要区别在于它们在编译时的处理方式。当使用 const 枚举时,TypeScript 编译器会在编译时尽可能地消除对枚举的引用,并直接内联枚举成员的值。这可以提高性能,并减少生成的代码大小。

const enum Color {
  Red,
  Green,
  Blue,
}
let myColor: Color = Color.Red; // 在TypeScript代码中,这是类型安全的 let myColor=0
// 假设你有一个接受Color类型参数的函数
function printColor(color: Color) {
  // ...
}
printColor(Color.Green); // 在TypeScript代码中,这也是类型安全的 printColor(1);
最后更新时间' 2025/1/3 14:16:58