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

相关推荐

  • Nuxt3_扫盲 入门与原理介绍【学习笔记】

    Nuxt 3 入门与原理介绍 💡 什么是 Nuxt 3? Nuxt 3 是基于 Vue 3 和 Vite 打造的全栈前端框架,支持: 服务端渲染(SSR) 静态站点生成(SSG) 单页应用(SPA) 构建全栈应用(支持 API) Nuxt 3 是 Vue 的“加强版”,帮你简化项目结构和开发流程。 🔧 核心原理 功能 Nuxt 如何处理 ✅ 页面路由 自动根…

    个人 2025年4月6日
    42000
  • 【开篇】

    我是Walker,生于八十年代初,代码与生活的旅者。全栈开发工程师,游走于前端与后端的边界,执着于技术与艺术的交汇点。 代码,是我编织梦想的语言;项目,是我刻画未来的画布。在键盘的敲击声中,我探索技术的无尽可能,让灵感在代码里永恒绽放。 深度咖啡爱好者,迷恋每一杯手冲的诗意与仪式感。在咖啡的醇香与苦涩中,寻找专注与灵感,亦如在开发的世界中追求极致与平衡。 骑…

    2025年2月6日 个人
    65000
  • 无畏前行,拳释力量 🥊💪

    拼搏,是一种态度 生活就像一场比赛,没有捷径可走,只有不断训练、突破、超越,才能站上属于自己的舞台。这不仅是一种对抗,更是一种自我的觉醒——敢于迎战,敢于挑战,敢于成为更强的自己。 运动中的拼搏精神 无论是拳击、跑步,还是力量训练,每一次出拳、每一次挥汗、每一次咬牙坚持,都是对身体与心灵的磨炼。拼搏不是单纯的对抗,而是一种态度——面对挑战,不退缩;面对失败,…

    个人 2025年2月26日
    41500
  • 深入理解ES6 011【学习笔记】

    Promise与异步编程 因为执行引擎是单线程的,所以需要跟踪即将运行的代码,那些代码被放在一个任务队列中,每当一段代码准备执行时,都会被添加到任务队列中,每当引擎中的一段代码结束执行,事件循环会执行队列中的一下个任务。 Promise相当于异步操作结果占位符,它不会去订阅一个事件,也不会传递一个回调函数给目标函数,而是让函数返回一个Promise,就像这样…

    个人 2025年3月8日
    36200
  • 深入理解ES6 008【学习笔记】

    迭代器(Iterator)和生成器(Generator) 这个新特性对于高效的数据处理而言是不可或缺的,你也会发现在语言的其他特性中也都有迭代器的身影:新的for-of循环、展开运算符(...)、甚至连异步编程都可以使用迭代器。 迭代器是一种特殊的对象,它具有一些专门为迭代过程设计的专有接口,所有的迭代器对象都有一个next()方法,每次调用都返回一个结果对…

    个人 2025年3月8日
    35100

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

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

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