添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

初学者技巧

  • 先书写 js 部分,再添加 ts 类型注解
  • 先写骨架,如:
    type AxiosType = () => void
    const obj = {}
  • 后加:
    type AxiosType= (config:{ url:string, method?:string}) => void
    const obj = { name:string, age:number }
  • 先删除掉函数的 ts 类型,解读功能,再去理解类型

TypeScript

TypeScript(简称:TS)是 JavaScript 的超集(JS 有的 TS 都有)。
TypeScript = Type + JavaScript(在 JS 基础之上,为 JS 添加了类型支持)。
TypeScript 是微软开发的开源编程语言,可以在任何运行 JavaScript 的地方运行。

TypeScript 相比 JS 的优势

  1. 更早(写代码的同时)发现错误,减少找 Bug、改 Bug 时间,提升开发效率。
  2. 程序中任何位置的代码都有代码提示,随时随地的安全感,增强了开发体验。
  3. 强大的类型系统提升了代码的可维护性,使得重构代码更加容易。
  4. 支持最新的 ECMAScript 语法,优先体验最新的语法,让你走在前端技术的最前沿。
  5. TS 类型推断机制,不需要在代码中的每个地方都显示标注类型,让你在享受优势的同时,尽量降低了成本。

安装编译 TS 的工具包

npm i -g typescript TS -> JS 的转化 tsc –V 检验是否成功

编译并运行 TS 代码

  1. 创建 hello.ts 文件(注意:TS 文件的后缀名为 .ts)。
  2. 将 TS 编译为 JS:在终端中输入命令,tsc hello.ts(此时,在同级目录中会出现一个同名的 JS 文件)。
  3. 执行 JS 代码:在终端中输入命令,node hello.js。

简化运行 TS 的步骤

使用 ts-node 包,直接在 Node.js 中执行 TS 代码。

npm i -g ts-node

使用: ts-node hello.ts

TS = JS +类型系统

可以显示标记出代码中的意外行为,从而降低了发生错误的可能性

为变量添加类型约束 let age:number = 18

常用基础类型

TS 新增类型

联合类型、自定义类型(类型别名)、接口、元组、字面量类型、枚举、void、any 等。

number/string/boolean/null/undefined/symbol

let name:string = 'wendy'

对象类型:object(包括,数组、对象、函数等对象)

数组类型的两种写法:(推荐使用 number[] 写法)
  • let numbers:number[] = [1,3,5]
  • let stings:Array<string> =['a','b']
数组中既有 number 类型,又有 string 类型
  • let arr:(number | string)[] = [1,'a']
    • 只有一根竖线,不要与 JS 中的或(||)混淆了

当同一类型(复杂)被多次使用时,可以通过类型别名,简化该类型的使用。✨命名规范:首字母大写

type CustomArray = (number | string)

let arr1 = CustomArray = [1,'a']

type IPerson = { 
    name:string
    age:number
    sayHi():void
 

函数参数和返回值的类型

1 单独指定参数、返回值的类型
  • const add = (num1:number):number=>{return num1}
2 同时指定参数、返回值的类型
  • const add:(num1:number)=>number=(num1)=>{return num1}
如果函数没有返回值,那么,函数返回值类型为:void。
  • const greet=(name:string):void=>{}

const mySlice(start?:number,end?:number):void{}

注意:可选参数只能出现在参数列表的最后,也就是说可选参数后面不能再出现必选参数。

类型别名 type

作用:用于存储 TS 类型,方便类型复用

语法: type 类型别名 = 具体类型

命名建议:使用大驼峰命名法,类型别名首字母大写

type MyType = (string | number)[]
let arr1:MyType
let arr2:MyType
  • 不仅可以描述对象,还可描述基本类型的属性
type 实现继承 &
type Point2D { x:number;y:number}
type Point3D = Point2D & { z:number }
 

TS 中对象的类型就是在描述对象的结构(有什么类型的属性和方法)

let person:{name:string;age:number;sayHi():void}={
    name:'jack',
    age:19,
    sayHi(){}
  • 多个属性类型时,使用 ;(分号)来分隔
  • 方法的类型也可以使用箭头函数形式{ sayHi: () => void }

我们在使用 axios({ … }) 时,如果发送 GET 请求,method 属性就可以省略。

const myAxios(config:{url:string;method?:string}){}

可选参数默认值:

const greet = (name: string, b = 10): void => {
  console.log(name, b)
greet('wendy')
 

当一个对象类型被多次使用时,一般会使用接口(interface)来描述对象的类型,达到复用的目的。

  1. 仅仅可以描述对象,在过去的框架中使用较多
  2. 使用 interface 关键字来声明接口。
  3. 声明接口后,直接使用接口名称作为变量的类型
  4. 因为每一行只有一个属性类型,因此,属性类型后没有 ;(分号)。
interface IPerson{
    name:string
    age:number
    sayHi():viod
let person1:IPerson={
    name:'jack',
    age:19,
    sayHi(){}
interface 实现继承 extends

如果两个接口之间有相同的属性或方法,可以将公共的属性或方法抽离出来,通过继承来实现复用。

interface Point2D { x:number;y:number}
interface Point3D extends Point2D { z:number }

interface(接口)和 type(类型别名)的对比:

都可以给对象指定类型。

接口 interface,只能为对象指定类型。
类型别名 type,不仅可以为对象指定类型,实际上可以为任意类型指定别名。

let position:number[] = [39.213,28.21]

使用 number[] 的缺点:不严谨,因为该类型的数组中可以出现任意多个数字。

更好的方式:元组

元组类型是另一种类型的数组,它确切地知道包含多少个元素,以及特定索引对应的类型

let position:[number,number]

发生类型推论的 2 种常见场景:

1 声明变量并初始化时

  • let age = 18

2 决定函数返回值时

  • const add =(num1:number){ return num1 }

这两种情况下,类型注解可以省略不写!
✨工作经验:能省略类型注解的地方就省略偷懒,充分利用TS类型推论的能力,提升开发效率)
✅技巧:如果不知道类型,可以通过鼠标放在变量名称上,利用 VSCode 的提示来查看类型

// 类型推导
// 初始化
let age = 10
// b = 1 默认值
const add2 = (b = 1) => age + b
// 返回值推导
console.log(add2()) // 11
 

使用类型断言来指定更具体的类型

getElementById 方法返回值的类型是 HTMLElement,该类型只包含所有标签公共的属性或方法,不包含 a 标签特有的 href 等属性。

const aLink = docment.getElementById('link') as HTMLAnchorElement
在这里插入图片描述

另一种语法,使用 <> 语法

const aLink = <HTMLAnchorElement> docment.getElementById('link')

❗️ 技巧:

在浏览器控制台,通过 console.dir(document.createElement("a")) 打印 DOM 元素,在属性列表的最后面,即可看到该元素的类型。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Image⇒ HTMLImageElement
a ⇒ HTMLAnchorElement

字面量类型

除字符串外,任意的 JS 字面量(比如,对象、数字等)都可以作为类型使用

const str = 'Hello TS'

字面量类型配合联合类型一起使用,用来表示一组明确的可选值列表

function changeDirection(direction:'up'|'down'|'left'|'right')

参数 direction 的值只能是 up/down/left/right 中的任意一个。

相比于 string 类型,使用字面量类型更加精确、严谨

定义一组命名常量。它描述一个值,该值可以是这些命名常量中的一个。

TS项目中可通过设计枚举结构,提高源代码的可读性
✅开发需求:
用户编辑界面,用户选择的性别 男 女 未知
后端接口参数要求传的是数字对应为 1 0 -1

enum Gender {
  Boy = 1,
  Girl = 0,
  UnKnown = -1
const query = {
  name: 'wendy',
  gender: Gender.Girl 
console.log('后端所需的参数', query)
// 数据反显
const res = {
	code:200,
	data:1
console.log('后端返回的参数回显', Gender[res.data]) // Boy

enum Direction { Up,Down,Left,Right}

function changeDirection(direction:Direction)

  1. 使用 enum 关键字定义枚举。
  2. 约定枚举名称、枚举中的值以大写字母开头。
  3. 枚举中的多个值之间通过 ,(逗号)分隔。
  4. 定义好枚举后,直接使用枚举名称作为类型注解。

changeDirection(Direction.Up)

默认为:从 0 开始自增的数值。

enum Gender1 {
  // 默认自增
  UnKnown = -1,
  Boy, // 0
  Girl,  // 1
字符串枚举

字符串枚举的每个成员必须有初始值。✨建议还是使用 字面量类型 + 联合类型 替代 字符串枚举

any 类型

❗原则:不推荐使用 any!可以对该值进行任意操作,并且不会有代码提示。

隐式具有 any 类型的情况:1 声明变量不提供类型也不提供默认值 2 函数参数不加类型。

typeof

可以在类型上下文中引用变量或属性的类型(类型查询)

let p = {x:1,y:2}

function formatPoint(point:typeof p){}

typeof 出现在类型注解的位置

注意:typeof 只能用来查询变量或属性的类型,无法查询其他形式的类型(比如,函数调用的类型)。

class 类

实例属性初始化:
class Person{ 
    age:number
    gender = 'man' // 初始化数据 可省略类型注解
    // 构造函数
	constructor(age:number,gender:string){
        this.age = age
        this.gender = gender
	// 实例方法:
	scale(n:number):void{
        this.x *= n
        this.y *=n
构造函数:⬆
  • 需要为构造函数指定类型注解,否则会被隐式推断为 any;构造函数不需要返回值类型。
实例方法:⬆
  • 方法的类型注解(参数和返回值)与函数用法相同。

1 extends(继承父类)

2 implements(实现接口)

通过 implements 关键字让 class 实现接口

interface Singable{
    sing():void
class Person implements Singable{
    sing(){}

Person 类实现接口 Singable 意味着,Person 类中必须提供 Singable 接口中指定的所有方法和属性。

类成员可见性:

控制 class 的方法或属性对于 class 外的代码是否可见。

可见性修饰符:

1 public(公有的)(可省略)

2 protected(受保护的)

  • 仅对其声明所在类和子类中(非实例对象)可见

3 private(私有的)。

  • 仅对其声明所在类和子类中(非实例对象)可见

readonly:表示只读,用来防止在构造函数之外对属性进行赋值。

类型兼容性

两种类型系统:

1 Structural Type System(结构化类型系统)

类型检查关注的是值所具有的形状,如果两个对象具有相同的形状,则认为它们属于同一类型。

class类型兼容性

对于对象类型来说,y 的成员至少与 x 相同,则 x 兼容 y(成员多的可以赋值给少的)。

成员多的 Point3D 可以赋值给成员少的 Point

class Point{x:number;y:number}

class Point3D{x:number;y:number;z:number}

const p:Point = new Point3D()

接口兼容性

接口之间的兼容性,类似于 class。并且,class 和 interface 之间也可以兼容。

函数兼容性

需要考虑:1 参数个数 2 参数类型 3 返回值类型。

  1. 参数少的可以赋值给参数多的
  2. 相同位置的参数类型要相同(原始类型)或兼容(对象类型)。
  3. 如果返回值类型是原始类型,此时两个类型要相同。
  4. 如果返回值类型是对象类型,此时成员多的可以赋值给成员少的

2 Nominal Type System(标明类型系统)(比如,C#、Java 等)

功能类似于接口继承(extends),用于组合多个类型为一个类型(常用于对象类型)
请添加图片描述

泛型是可以在保证类型安全前提下,让函数等与多种类型一起工作,从而实现复用,常用于:函数、接口、class 中。
一般不确定的地方就可以先丢泛型

理解为是一个函数,将类型当做参数传递

  1. 创建泛型函数:
    const id = <T>(value: T): T => {
     return value;
    
  2. 调用泛型函数:
    const num = id<number>(10)
    const str = id<string>('a')
    
  3. 简化调用泛型函数:✨建议使用
    let num = id(10)
    let str = id('a')
    
  4. 多泛型变量
    const fn = <T, U>(a: T, b: U) => {};
  5. 泛型约束:

    指定更加具体的类型

    const id = <T>(value: T[]): T[] => {
    	console.log(val.length);
    	return value;
    // ✨✅添加约束
    interface ILength{
    	length:number
    // T extends ILength 添加泛型约束
    // 解释:表示传入的 类型 必须满足 ILength 接口的要求才行,也就是得有一个 number 类型的 length 属性
    const fn = <T extends ILegnth>(val:T):T{
    	console.log(val.length);
    	return val
    
  6. 泛型接口:
    // 定义泛型接口,接口就是描述对象的
    interface Res<T> {
    	msg: string;
    	code: number;
    	result: T;
    // 传递具体的类型
    const res:Res<number[]> = {
    	msg: "成功",
    	code: 200,
    	result: [1, 2, 3]
    res.result.push(4);
      
    1. 创建泛型类:
    2. 泛型工具类型:
/* 1. 需求: 封装一个函数,传入一个参数,包装成数组返回(可能是 number/string/boolean 等)
	fn(20)  =>  [20]
	fn(true)  =>  [true] */
const fn = <T>(val:T):T =>{
	return [val]
/* 2. 需求: 封装一个函数,传入数组, 交换数组两项的位置(可能是 number/string/boolean 等)
	fn([true,20])  =>  [20,true] */
const fn = <T,K>(arr:[T,K]):[K,T] =>{
	return [arr[1],arr[0]]
/* 3. 一般后台返回的数据 res 中,result 是 null 或者数组,数组每一项是一个固定的结构对象,{id:1,name:"吃饭",done:false}封装符合的 Res 泛型接口 */
interface Res<T> {
	msg:string,
	code:number,
	result:T
const res1:Res<null> = {
	msg:'success',
	code:200,
	result:null
interface ResultType {
	id:number,
	name:string,
	done:boolean
const res2:Res<ResultType[]> = {
	msg:'success',
	code:200,
	result:[{
		id:1,
		name:'wendy',
		done:false
keyof 关键词

关键字接收一个对象类型,生成其键名称 字符串的联合类型

interface Person {
  name: string;
  age: number;
  gender: string;
const str: keyof Person = "gender";
// 两行代码是等价的
const myStr: "name" | "age" | "gender" = "gender";

ts 默认值为 unknown ----------对应js的 undefined
✨一般使用场景,用在 对象 的泛型封装中
const fn1 = <T = 1>(a: T) => {};

ts类型注解

需要添加导入、导出或空的export {}语句来使当前文件成为模块

:number就是类型注解,为变量提供类型约束

string number boolean undefined null

❗原始类型首字母是小写

✨项目一般不会主动声明undefined和null,一般在出现错误的时候回看到

作用:约束数组每一项的类型

✨推荐语法:类型[]

let arr:number[] = [1,2,3]

❌类型 string 的参数不能赋给类型 number 的参数 arr.push('1')

❌类型 number 的参数不能赋给类型 string 的参数

语法:一根竖线

let arr:(string | number)[] 或者 let guess:string | number[]

  • 解读:数组类型的每一项为 字符串类型或数值类型

❌不能将类型boolean分配给类型(string | number)[]

❌❗不能将类型string[]分配给类型string | number[]

  • 函数参数的类型
  • 函数返回值的类型