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

相关推荐

  • 深入理解ES6 002【学习笔记】

    字符串和正则表达式 字符串和正则表达式 Javascript字符串一直基于16位字符编码(UTF-16)进行构建。每16位的序列是一个编码单元(code unit),代表一个字符。length、charAt()等字符串属性和方法都基于这个编码单元构造的。Unicode的目标是为世界上每一个字符提供全球唯一的标识符。如果我们把字符长度限制在16位,码位数量将不…

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

    Set集合与Map集合 在js中有g一个in运算符,其不需要读取对象的值就要以判断属性在对象中是否存在,如果存在就返回true。但是in运算符也会检索对象的原型,只有当对象原型为null时使用这个方法才比较稳妥。 Set集合 let set = new Set() set.add(5) set.add("5") console.log(s…

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

    代理(Proxy)和反射(Reflection)API 代理是一种可以拦截并改变底层javascript引擎操作的包装器,在新语言中通过它暴露内部运作对象,从而让开发者可以创建内建的对象。 代理陷阱 覆写的特性 默认特性 get 读取一个属性值 Reflect.get() set 写入一个属性值 Reflect.set() has in操作符 Reflect…

    个人 2025年3月8日
    74000
  • TS珠峰 002【学习笔记】

    泛型 /* * @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git &a…

    个人 2025年3月27日
    90400
  • 热爱运动,挑战极限,拥抱自然

    热爱 在这个快节奏的时代,我们被工作、生活的压力所包围,常常忽略了身体的需求。而运动,不仅仅是一种健身方式,更是一种释放自我、挑战极限、与自然共舞的生活态度。无论是滑雪、攀岩、冲浪,还是跑步、骑行、瑜伽,每一种运动都能让我们找到内心的激情,感受到生命的跃动。 运动是一场自我挑战 挑战极限,不仅仅是职业运动员的专属,而是每一个热爱运动的人都可以追求的目标。它可…

    个人 2025年2月26日
    80800

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

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

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