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


TS基础

初学者技巧

书写技巧

  • 先书写 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

typescript最新版本 typescript !_typescript

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

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

❗️ 技巧:

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

typescript最新版本 typescript !_typescript最新版本_02


typescript最新版本 typescript !_javascript_03


typescript最新版本 typescript !_前端_04

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),用于组合多个类型为一个类型(常用于对象类型)

typescript最新版本 typescript !_泛型_05

泛型

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

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

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

指定更加具体的类型

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
}
  1. 泛型接口:
// 定义泛型接口,接口就是描述对象的
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. 泛型类:
  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";

typescript最新版本 typescript !_typescript最新版本_06

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

TS回顾

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[]

函数类型

基本用法

定义
  • 函数参数的类型
  • 函数返回值的类型
写法
  • 在函数基础上 分别指定 参数和返回值类型
const res = (num1:number,num2:number):number => {
    return num1 + num2
}
  • 使用类型别名 同时指定 参数和返回值
type AddType = (num1:number,num2:number) => number
const add1:AddType = (num1,num2) =>{
    return num1 + num2
}

函数类型-void类型

用于标注函数没有返回值

const sayHi = ():void =>{
    console.log('hello')
}

❗ 返回值类型不为 void 的函数,必须有返回值

const sayHi2 = ():undefined =>{
    console.log('hello')
    return 
}

函数类型-可选参数

语法: 参数后面 + ?

❗ 必选参数不能位于可选参数后

const fn = (n?:number):void => {
fn(10)
const mySlice(start?:number,end?:number):void => {
	console.log(`开始索引为${start},结束索引为${end}`)
mySlice()
mySlice(1)
mySlice(1, 2)

对象类型

描述对象的结构,规定对象属性的方法和类型

基本用法

❌ 空对象 let obj1: {} = { name: 'zs' } let obj2: object = { age: 18 }

❌存在问题:不指定对象属性和具体类型,无法访问对象的属性

✅ 给对象的属性指定类型

let obj3: { name:string } = { name:'zs' }

✅给对象的多个属性指定类型,一行书写用 ; 隔开,如果换行可以不写 ;

let obj4:{
    name:string
    age:number
}

拓展用法

方法可使用箭头函数
使用类型别名方便复用,提高代码复用性
属性和方法设置可选

🔔小练习:

1. 声明一个既有属性又有方法的对象类型
type ObjType = {
    name:string
    age:number
    sayHi():void
    sayNo:() => void
let obj5: ObjType = {
    name:'zs',
    age:19,
    sayHi(){},
    sayNo(){}
}
2. 为 axios({ url, method }) 添加 ts 类型注解

PS:如果是 get 请求 method 可以省略

const axiosType = {
    url:string
    method?:string
const axios = (config:axiosType) => {
    console.log(config)
}
3. 创建一个学生对象,该对象中具有以下属性和方法:
  • 属性:必选属性:姓名、性别、成绩,可选属性:身高
  • 方法:学习、打游戏(可选)
const StuType = {
    name:string
    gender:string
    score:number
    height?:number
    study():void
    playGame?():void
const stu1:StuType = {
    name: '悟空',
    gender: '男',
    score: 100,
    height: 150,
    study() {
        console.log('打妖怪')
    playGame() {
        console.log('playGame~')
}

测试必选属性和方法:

stu1.name.trim()
stu1.score.toFixed(2)
stu1.study()

❗❗ 注意:可选属性和方法需要先判断再使用

if(stu1.height){
    stu1.height.toFixed(2)
if (stu1.playGame) {
  stu1.playGame()
}

对象语法补充-可选链操作符

经验:

可选属性或方法,可配合 ?. 可选链操作符 安全使用。

安全调用写法1:判断

if(stu1.playGame){
    stu1.playGame()
}

安全调用写法2:短路运算

stu1.playGame && stu1.playGame()

✅安全调用写法3:可选链操作符,最简洁

stu1.playGame?.()

接口类型

用来定义对象的结构,type 和 interface 都可以定义对象的结构 ---- 按团队的习惯来即可

基本用法

interface IsStudent{
    name:string
    age?:number
    sayHi():voids
const stu1:IsStudent = {
}

通过 type(类型别名)定义对象的结构

type TsStudent = {
    name:string
    age?:number
    sayHi():void
}

扩展—接口继承

interface c{
    x:number
    y:number
// 先继承 Point2D 的所有属性和方法,在花括号内拓展添加自己的属性或方法
interface Point3D extends Piont2D{
    z:number
}

扩展—类型别名和交叉类型

type Point3D = Point2D & { z:number }

type 和 interface

共同点:

都可以定义对象类型,在大多数情况下,可以根据个人(团队)喜好进行选择

区别:

type 不能出现重名

interface 重名会自动合并

TS自动类型推断

声明变量并提供初始值
函数的返回值

let name = 'tony'

name = 123 根据初始值,自动推导的类型为 string

TS 字面量类型

基础语法:

TS 中允许把字面量直接当做类型使用

let name:'tony' = 'tony'

应用场景:

✨字面量类型可匹配联合类型使用,实现代码提示和类型校验

let method:'GET'|'POST'|'DELETE'|'PUT'

🔔练习实战:升级 axios 的 method 属性

type Config = {
    url:string
    // method?:string ❌范围太大了,没有代码提示,单词写错了不知道
    method?:'GET'|'POST'|'DELETE'|'PUT'
// 为 axios 的参数添加类型注解
const axios = (config:Config) => {
    console.log(config)
}

any类型

显式any:

逃避 TS 的类型检查,相当于让代码变回 JS,不检查类型

let obj:any
// ❗风险:以上的代码虽然 书写时 没有报错提示,但是代码 运行时 是可能出现错误的
obj = { name: 'zs', age: 18 }
obj.trim()
隐式any:

声明变量(函数参数),但没有设置类型或初始值

const add = (n1,n2)=>{ return n1+n2 }

✨经验:
  1. 第一原则:尽量避免使用 any 类型,any 使用得越多,程序漏洞越多
  2. 如果使用第三方库不知道是什么类型,可以先用 any 逃避 TS 的检查,让程序先跑起来

泛型(类型变量)

作用:提高代码的复用性

语法:先声明,再使用,T 表示类型变量

声明类型变量: <T> 使用类型变量: T

泛型别名

后端返回值类型,通过泛型提升可复用性

<T> 声明泛型
type ResponseType<T> = {
    msg:string
    code:number
    data:T //  T 使用泛型,使用前需声明
type User = {
    name:string
    age:number
}
应用
const userRes:ResponseType<User> = {
    msg: 'ok',
    code: 200,
    data: {
      name: 'zs',
      age: 18,
}
项目中更安全的访问

userRes.data.name

泛型接口

interface IdFn<T>{
    getId():T
    getIds():T[]
const idFn:IdFn<number> = {
    id:123,
    getId(){
        return 123
    getIds(){
        return [1,2]
}

泛型函数

function getId<T>(id:T){
    return id
 const res1 = getId<number>(1)
 const res2 = getId<string>('wendy')
 // ✨调用的时候,可以根据实参推导出参数的类型,可以省略不写
 const res3 = getId(123)

泛型约束 extends

约束 T 的类型范围是 ‘GET’ | ‘POST’
function fn1<T extends 'GET' | 'POST'>(params:T)T{
    return params
}
约束 T 的类型必须有 length 属性
function fn2<T extends { length:number }>(params:T):T{
    console.log(param.length)
    return param
}

封装抽离写法

interface Len{
    length:number
function fn3<T extends Len }>(params:T):T{
    console.log(param.length)
    return param
fn2([1, 2, 3])
fn3('123')