TS珠峰 001【学习笔记】

课程大纲

  • 搭建 TypeScript 开发环境。
  • 掌握 TypeScript 的基础类型,联合类型和交叉类型。
  • 详细类型断言的作用和用法。
  • 掌握 TypeScript 中函数、类的类型声明方式。
  • 掌握类型别名、接口的作用和定义。
  • 掌握泛型的应用场景,熟练应用泛型。
  • 灵活运用条件类型、映射类型与内置类型。
  • 创建和使用自定义类型。
  • 理解命名空间、模块的概念已经使用场景。
  • 详细 TS 中的类型保护、装饰器。
  • 巧妙运用类型推导和化简代码。
  • 深入理解 TypeScript 类型层级系统。
  • 详细函数的变与逆变。
  • 深入研究 infer 的用法与技巧。
  • 详细探究符号类型。
  • 灵活编写与运用类型声明文件,扩展 TypeScript 的类型系统。
  • 掌握 TS 中类型文件查找规则。
  • 深刻使用装饰器,运用反射元数据扩展装饰器的功能,实现控制反转、依赖注入。
  • 深度解析 TSConfig 配置文件。
  • TS 类型体操

ts基础

目前大部分企业的中大型前端项目都采用了 Typescript,那么为什么我们需要它?

JavaScript 的核心特点就是灵活,但随着项目规模的增大,灵活反而增加开发者的心智负担。例如在代码中一个变量可以被赋予字符串、布尔、数字,甚至是函数,这样就充满了不确定性。而且这些不确定性可能需要在代码运行的时候才能被发现,所以我们需要类型的约束。

当然不可否认的是有了类型的加持多少会影响开发效率,但是可以让大型项目更加健壮

  • Typescript 更像后端 JAVA,让 JS 可以开发大型企业应用;
  • TS 提供的类型系统可以帮助我们在写代码时提供丰富的语法提示;
  • 在编写代码时会对代码进行类型检查从而避免很多线上错误;

越来越多的项目开始拥抱 TS 了,典型的 Vue3、Pinia、第三方工具库,后端 NodeJS 等。我们也经常为以上编程拥有了更好的支持去编写 .d.ts 文件。

1. ts是一门语言

  1. ts 是 js 的一个超集,扩展了语法添加了静态类型支持以及其他一些新特性

环境配置

全局安装

npm install typescript -g
# 我们可以用tsc来把ts转成js
# 我们要把什么转换成什么,所以我们要先生成一个配置
# 在当前项目下创建一个配置文件tsc --init
# --watch
tsc --init
# 插件code runner这个插件 需要额外安装一个ts-node的包
 npm init -y
 npm install typescript rollup -D
 npm install rollup-plugin-typescript -D
 npm i @rollup/plugin-node-resolve rollup-plugin-serve -D
#  rollup.config.js
# error lens 插件

学习ts就是学习类型,ts的类型分类(DOM,Promise,原始方法)基础类型,
ts中:后面的都是类型 = 后面的都是值
ts 一切从安全的角度出发,看能不能赋值,就看安全不安全
ts 还有自动的类型推导,不用见到变量就写类型,而是推断的不正确,我们才需要自己编写

基础类型

let abc: string = 'Hello World';
console.log(abc);

let name = 'John Doe'; // 当前模块中,作用域隔离
let age = 20;
let handsome: boolean = true;
// 原始类似标识都是小写的,而类名都是大写的它描述的实例(都是对象)
// 类型注解:告诉TS变量的类型
// 类型推断:TS会自动尝试分析变量的类型
// 类型注解优先级高于类型推断
// 类型注解:变量名: 类型 = 值
// 类型推断:变量名 = 值
let s1: string = 'Hello World';
let s2: String = new String('Hello World');
let s3: String = 'Hello World'; // 会自动转换为对象
// 数组声明
let arr1: number[] = [1, 2, 3];
let arr2: Array<number> = [1, 2, 3];
// 元组:固定长度的数组
let tuple: [string, number] = ['Hello', 123];
// 元组的越界问题
// tuple[2] = 'World'; // 会报错
let tuple2: readonly [string, number, boolean] = ['Hello', 123, true];
// tuple2[0] = 'World'; // 会报错
export { name };

枚举(自带类型的对象)

let abc: string = 'Hello World';
console.log(abc);

let name = 'John Doe'; // 当前模块中,作用域隔离
let age = 20;
let handsome: boolean = true;
// 原始类似标识都是小写的,而类名都是大写的它描述的实例(都是对象)
// 类型注解:告诉TS变量的类型
// 类型推断:TS会自动尝试分析变量的类型
// 类型注解优先级高于类型推断
// 类型注解:变量名: 类型 = 值
// 类型推断:变量名 = 值
let s1: string = 'Hello World';
let s2: String = new String('Hello World');
let s3: String = 'Hello World'; // 会自动转换为对象
// 数组声明
let arr1: number[] = [1, 2, 3];
let arr2: Array<number> = [1, 2, 3];
// 元组:固定长度的数组
let tuple: [string, number] = ['Hello', 123];
// 元组的越界问题
// tuple[2] = 'World'; // 会报错
let tuple2: readonly [string, number, boolean] = ['Hello', 123, true];
// tuple2[0] = 'World'; // 会报错

// 枚举 自带类型的对象,自动增长
enum USER_ROLE {
  USER,
  ADMIN = 6,
  MANAGER,
  OTHER = 'other', // 异构枚举
}
/*
  USER = 0,
  ADMIN = 6,
  MANAGER = 7,
*/
console.log(USER_ROLE.USER); // 0
// 如果不需要对象可以直接采用常量枚举
const enum USER_ROLE2 {
  USER,
  ADMIN,
  MANAGER,
}
console.log(USER_ROLE2.USER); // 0
export { name };

null 和 undefined

// 严格模式下,不允许使用 any 类型
// nul 和 undefined 是任何类型的子类型
let x: number | null | undefined = 1;
// let y: number = undefined; // 会报错
// let z: number = null; // 会报错
// strict:false 可以使用 any 类型

never类型

永远不

// never 类型 它是任何类型的子类型,也可以赋值给任何类型
// never 类型的变量只能被赋值为 never 类型
function fn1(): never {
  throw new Error('报错了');
}
// 类型保护,保障程序的不缺失
// never 是永远到达不了的终点
function fn2(): never {
  while (true) {}
}
function validate(x: never) {
  console.log(x);
}
// 针对不同的类型做不同的处理
function fn3(x: number | string | boolean) {
  // 类似有收敛的作用
  if (typeof x === 'number') {
    console.log(x.toFixed(2));
    return;
  }
  if (typeof x === 'string') {
    console.log(x.trim());
    return;
  }
  if (typeof x === 'boolean') {
    console.log(x.valueOf());
    return;
  }
  //守卫语句
  validate(x); // 如果类型不匹配,会报错 这个逻辑应该是永远不会执行的所以上在还是有没覆盖到的逻辑(要添加boolean类型的判断)
}

object的类型

// object 类型 object {} Object
let obj: object = { name: 'John Doe' };
// obj.name = 'Jane Doe'; // 会报错
// 小写的 object 是对象类型,大写的 Object 是对象的构造函数 

!号非空断言

联合类型

// 一般我们会基于额外的类型来扩展定义类型 有点类似枚举
type Direction = "up" | "down" | "right" | "left"
let direction :Direction = "left"
// type和interface的区别
type women =
  | {
      wealthy: true;
      waste: string;
    }
  | {
      wealthy: false;
      norality: string;
    };
// 是富人一定不是节俭的人,是节俭的人一定不是富人

可以利用联合类型来做到属性之间的互斥

断言

断言有可能出问题,出问题后果自负

type women =
  | {
      wealthy: true;
      waste: string;
    }
  | {
      wealthy: false;
      norality: string;
    };
// 是富人一定不是节俭的人,是节俭的人一定不是富人(类比约定,不严谨)
// 断言 把某个类型断言为为已经存在的一种类型
let ele = document.getElementById('app');
ele!.innerHTML = 'Hello World'; // 绕过TS的类型检查 ele?.innerHTML = 'Hello World'; // 可选链 操作符(取值)
// false || true = true
// false && true = false
// null ?? 'Hello World' = 'Hello World' // 空值合并操作符
// as 类型断言 可以强制把某个类型断言为另外一个类型 as 类型
// 双重断言,我们可以把一个值断言成为 any 类型,然后再断言成为另外一个类型
// 类型断言不是类型转换,只是告诉TS编译器,这个值是什么类型
// 类型断言只在编译阶段起作用,不会影响真实的值

函数

// 函数类型

// 函数声明 function定义 函数表达式
function sum(a: number, b: number): number {
  return a + b;
}
let sum2 = function (a: number, b: number): number {
  return a + b;
};
let sum3: (a: number, b: number) => number = function (a, b) {
  return a + b;
};
// 类型比较长可以写成下面
type Sum = (a: number, b: number) => number;
let sum4: Sum = function (a, b) {
  return a + b;
};
// 会根据上下文推导赋予值的类型
let sum5: Sum = (a, b) => a + b;

// 常见的类型推导的方式
// 从右向左 根据赋值进行推导
let name = "jianz"
let age = 20
// 根据返回值进行类型推导
// void 表示不关心返回的具体类型(不校验)
// 可选参数(?)
let sum6 = (a: string = 'a', b?: string): string => {
  return a + b;
}; 

// 对象类型
let person = {
  name: 'John Doe',
  age: 20,
};
// ts中的this类型需要手动指定,默认是函数的第一个参数
function getVal(this: typeof person, key: keyof typeof person) {
  return this[key]; // this指向调用者,必须要有类型断言才能使用{
}
let r = getVal.call(person, 'name');
console.log(r);

重载

// 重载 (一般是有限操作), 它只是一个伪重载,只是一个类型的重载
function toArray(value: number): number[];
function toArray(value: string): string[];
function toArray(value: number | string) {
  if (typeof value === 'string') {
    return value.split('');
  } else {
    return value
      .toString()
      .split('')
      .map((item) => parseInt(item));
  }
}
let ar = toArray(123);
console.log(ar);
let ar1 = toArray('123');
console.log(ar1);

类(本身就可以充当类型)

它可以描述实例(类类型)

// 类
class Circle {
  // 类的属性 ts中所有的属性都要先声明再使用
  PI = 3.14;
  // 类的构造函数
  constructor(public radius: number) {
    this.radius = radius;
  }
  // 类的方法
  area() {
    return this.PI * this.radius ** 2;
  }
}
// 还有一种写法
class Animals {
  constructor(public name: string) {
    // this.name = name; // 赋值也可以省去
  }
  eat() {
    console.log(this.name + ' is eating');
  }
}
const a1 = new Animals('dog');
a1.eat();
// 访问修饰
// public 公共的 默认的
// private 私有的 只能在类的内部访问
// protected 受保护的 只能在类的内部和子类中访问
// readonly 只读的 初始化之后不能修改
// static 静态的
// abstract 抽象的

// #号开头的属性是私有属性 只能在类的内部访问 不能在类的外部访问(这个是一个js语法)
// 不能new
class Singleton {
  private static instance: Singleton;
  private constructor() {}
  static getInstance() {
    if (!this.instance) {
      this.instance = new Singleton();
    }
    return this.instance;
  }
}
let sg1 = Singleton.getInstance();
// 抽象类不能new
// 不能实例化,只能被继承
// 抽象类中可以拥有具体宾实现
abstract class Animal {
  constructor(public name: string) {}
  abstract eat(): void; // 原型上的方法 默认采用这种方法更好些
  abstract play:()=>void; // 实例上的方法 ts中不做区分,但是一般的是实例方法
}

接口

接口可以理解为对行为的抽象(没有具体的实现) ,可以用于描述 对象,函数,类,混合类型

// 接口是对行为的抽象,而抽象类是对类的抽象
// 用接口描述函数
// interface 描述的是形状或结构
interface IFullname {
  firstname: string;
  lastname: string;
}
type IFn = (obj: IFullname) => string;
let fn: IFn = ({ firstname, lastname }: IFullname): string => {
  return firstname + lastname;
};
fn({ firstname: 'John', lastname: 'Doe' });
// 1. 如果只是用来描述结构我们采用interface
// 2. 如果涉及到联合类型,则只能使用type
// 3. type不能扩展,interface可以扩展
// 4. type不能重名,interface可以重名
// 5. type可以使用typeof获取实例的类型,interface不行
// 6. type可以使用keyof获取key的类型,interface不行
// 7. type可以使用infer获取函数返回值的类型,interface不行
// 8. type可以使用extends实现交叉类型,interface不行
// 9. type可以使用条件类型,interface不行
// 10. type可以使用映射类型,interface不行
// 11. type可以使用索引访问操作符,interface不行

// 可以用接口描述混合类型
interface IFn1{
  (a:number,b:number):number;
  prop1:string;
  prop2:number;
}
// let fn11:IFn1 = (a,b)=>a+b; // 这样就报错了,因为fn11没有prop1和prop2属性且let定义可以重新被赋值
const fn11:IFn1 = (a,b)=>a+b;
fn11.prop1 = 'Hello';
fn11.prop2 = 123;
// 一般情况下我们使用interface来描述对象,如果需要使用联合类型,交叉类型,元组,只能使用type
interface IVeg {
  readonly color: string;
  size: number;
  taste?: 'sweet' | 'sour' | 'bitter'; // 可选属性
}
let veg: IVeg = {
  color: 'red',
  size: 20,
};
// veg.color = 'green'; // 会报错

如果对象中的属性,多于接口定义的,

  1. 可以采用断言来赋值
  2. 可以基于接口的特性写一个同名的接口
  3. 产生新类型,通过继承原有属性的方式
  4. 类型兼容
  5. [key:string]:any 任意类型扩展
interface ICar {
  color: string;
  a: 1;
  b: 2;
}
type ValueOf = ICar[keyof ICar]; // 1 | 2 | string

主题测试文章,只做测试使用。发布者:Walker,转转请注明出处:https://joyjs.cn/archives/4409

(0)
Walker的头像Walker
上一篇 2025年3月8日 02:11
下一篇 2025年3月27日 15:01

相关推荐

  • Go工程师体系课 004【学习笔记】

    需求分析 后台管理系统 商品管理 商品列表 商品分类 品牌管理 品牌分类 订单管理 订单列表 用户信息管理 用户列表 用户地址 用户留言 轮播图管理 电商系统 登录页面 首页 商品搜索 商品分类导航 轮播图展示 推荐商品展示 商品详情页 商品图片展示 商品描述 商品规格选择 加入购物车 购物车 商品列表 数量调整 删除商品 结算功能 用户中心 订单中心 我的…

    个人 2025年11月25日
    10700
  • 深入理解ES6 003【学习笔记】

    函数 参数默认值,以及一些关于arguments对象,如何使用表达式作为参数、参数的临时死区。 以前设置默认值总是利用在含有逻辑或操作符的表达式中,前一个值是false时,总是返回后面一个的值,但如果我们给参数传0时,就有些麻烦。需要去验证一下类型 function makeRequest(url,timeout,callback){ timeout = t…

    个人 2025年3月8日
    1.1K00
  • Go工程师体系课 006【学习笔记】

    项目结构说明:user-web 模块 user-web 是 joyshop_api 工程中的用户服务 Web 层模块,负责处理用户相关的 HTTP 请求、参数校验、业务路由以及调用后端接口等功能。以下是目录结构说明: user-web/ ├── api/ # 控制器层,定义业务接口处理逻辑 ├── config/ # 配置模块,包含系统配置结构体及读取逻辑 …

    个人 2025年11月25日
    11700
  • 深入理解ES6 010【学习笔记】

    改进的数组功能 new Array()的怪异行为,当构造函数传入一个数值型的值,那么数组的length属性会被设为该值;如果传入多个值,此时无论这些值是不是数值型的,都会变为数组的元素。这个特性另人困惑,你不可能总是注意传入数据的类型,所以存在一定的风险。 Array.of() 无论传多少个参数,不存在单一数值的特例(一个参数且数值型),总是返回包含所有参数…

    个人 2025年3月8日
    1.1K00
  • Go工程师体系课 018【学习笔记】

    API 网关与持续部署入门(Kong & Jenkins) 对应资料目录《第 2 章 Jenkins 入门》《第 3 章 通过 Jenkins 部署服务》,整理 Kong 与 Jenkins 在企业级持续交付中的实战路径。即便零基础,也能顺着步骤搭建出自己的网关 + 持续部署流水线。 课前导览:什么是 API 网关 API 网关位于客户端与后端微服务…

    个人 2025年11月25日
    9400

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信
欢迎🌹 Coding never stops, keep learning! 💡💻 光临🌹