环境配置
安装 npm install typescript -g
查看版本 tsc --version
1.初识typescript
邂逅typescript,typescript的基本使用
新建ts 文件 (记得导出)
typescript 定义时可指定 变量类型 在名称后面加引号 和 类型
格式为 let 名称: 类型 = 值 (例如: let name:string = ‘张三’)
类型 string 首字母为小写
let message: string = '张三'
console.log(message, '------')
let name = 123
name = 66
export {}
运行 tsc xxxx 并在html中引入
<html>
<head></head>
<body>
<h1>哈哈哈哈</h1>
</body>
<script src="xxxxx"></script>
</html>
typescript的类型推导
在 ts 中不定义 类型 会默认赋值 值的类型
例如 let name = 123 则默认 name的类型是 number
但 使用 const 赋值时 会默认返回 字面量类型
例如 const name = 1.88 则默认 name的类型是 1.88 等同于 const name:1.88 = 1.88
所以:let 推导出来的是通用类型,const推导出来的是字面量类型
2.typescript的数据类型
js中的类型
Array
//明确的指定<数组>的类型注解:两种写法
// 1.string[]: 数组类型,并且数组中存放的是字符串
let list: string[] = ['张三','李斯']
list.push('王五')
//2. Array<number> 数组类型,并且数组中存放的是number
let arr:Array<number> = [0,1,2]
arr.push(9)
export {}
Object
let info: {
name: string
age: number
} = {
name: '青子',
age: 18
}
console.log(info.name)
// 可以通过 type 定义
type infos = {
names: string
ages: number
}
let info1: infos = {
names: '陆景',
ages: 18
}
console.log(info1.ages)
export { }
使用
// z? 为可传值,不是必传
type obj = {
x: number
y: number
z?: number
}
function getNum(val: obj) {
return val.x + val.y
}
getNum({ x: 1, y: 2 })
export { }
函数的类型
参数
function getInfo(num1:number,num2:number){
return num1 + num2
}
getInfo(5,6)
export {}
返回值
//返回值可以明确指定,也可以自动进行推导
function getInfo(num1:number,num2:number):number{
return num1 + num2
}
let val = getInfo(5,6)
console.log(val)
export {}
匿名参数
const names = ['abc','cds','opp']
//匿名函数最好不要添加注解类型
names.forEach(function(item,index,arr){
console.log(item,index,arr)
})
export {}
使用
type val = {
time: number
text: string
}
function getList(value: string) {
let arr: val[] = []
arr.push({ time: 111, text: '你好' })
return arr
}
let list = getList('oioioio')
list.map(item => {
console.log(item.time, item.text)
})
export {}
ts中的类型
any
//any表示不限制任意类型
let id: any = 44
id = "text"
id = []
console.log(id.length)
export { }
unknown
let foo:unknown = 'pp'
foo = 123
//要求必须进行类型的校验
if( typeof foo === 'string'){
console.log(foo.length)
}
export{}
void
// 没有返回值的时候
function foo(num:number,num1:number):void{
console.log(num+num1)
}
export {}
never
// 一般情况下不会使用never ,never表示不会返回任何值
// 封装工具的时候可以使用
function getFoo(message: number | string) {
switch (typeof message) {
case "string":
console.log(message.length)
break
case "number":
console.log(Number(message))
break
default:
const check: never = message
}
}
getFoo(123)
export{}
tuple
//元组类型,元组数据结构中可以存放不同的数据类型,取出来的item也是有明确的类型
const info:[string,number,number] = ['zs',18,1.88]
const value = info[1]
export{}
3. typescript的语法细节
联和类型的使用
//有的情况下一个值可能是number 也可能是string 类型,这种情况下就可以使用联合类型
let id: number | string = 123
id = 'aaa'
//范围缩小
if(typeof id === 'string'){
console.log(id.length)
}
export{}
类型别名的使用type
// type 类型的别名
type val = number | string
let id: val = '123'
id = 456
export { }
接口声明的使用interface
//type 直接赋值
type info = {
x: number
y: number
z?: number
}
//interface 声明
interface info1 {
x: number
y: number
z?: number
}
function getFoo(val:info1){
return val.x + val.y
}
getFoo({x:1,y:2})
export{}
别名和接口的区别
//区别一:type类型使用范围更广,接口类型只能用来声明对象
type info = number
type info1 = string[]
//区别二 : interface 可以多次声明,可以继承
interface Ikun {
x: number
y: number
}
interface Ikun {
z?: number
}
export { }
交叉类型的使用
//交叉类型,满足两个或多个条件
interface info {
name: string,
age: number
}
interface Ikun {
running: () => void
}
type NewType = info & Ikun
let list: NewType = {
name: '张三李斯',
age: 15,
running: function () {
return '你干嘛!哎哟'
}
}
export {}
类型断言
// 获取DOM 元素 <img calss="img"/>
//使用类型断言
//类型断言规则:断言只能断言成更加具体的类型,或者不太具体(any/unknown)类型
const imgEl = document.querySelector(".img") as HTMLImageElement
imgEl.src = "xxx"
imgEl.alt = 'yyy'
export {}
非空类型断言
interface info {
name:string
age:18
friend?:{
name:string
}
}
let foo:info = {
name:'张三',
age:18
}
// 访问属性:可选链:?.
console.log(foo.friend?.name)
// 属性赋值
// 解决方案一:范围缩小
if(foo.friend){
foo.friend.name = '言念'
}
// 解决方案二:非空类型断言(有点危险,只有在确保friend 一定有值时才能使用
foo.friend!.name = '言念'
export{}
字面量类型
type utils = { url: string, method: "post"|"get" }
let request:utils = {url:'xxxx',method:'post'}
request.method as "post"
export {}
类型缩小
//1 typeof
type id = string | number
let foo: id = "123"
if (typeof foo === "string") {
console.log(foo.length)
}
// 2 ===/!==
type Direction = "left" | "right" | "top" | "bottom"
function switchDirection(direction: Direction) {
if (direction === "left") {
console.log("左")
} else if (direction === "right") {
console.log("右")
} else if (direction === "top") {
console.log("上")
}
else if (direction === "bottom") {
console.log("下")
}
}
// 3 instanceof 传入一个日期,打印日期
function printDate(date: string | Date) {
if (date instanceof Date) {
console.log(date.getTime())
} else {
console.log(date)
}
}
// 4 in 判断是否有一个属性
interface Iswim {
swim: () => void
}
interface Irun {
run: () => void
}
function move(animal: Irun | Iswim) {
if ("swim" in animal) {
animal.swim()
} else if ("run" in animal) {
animal.run()
}
}
const fish: Iswim = {
swim: function () { }
}
const dog: Irun = {
run: function () { }
}
move(dog)
export { }
4.TypeScript函数类型
函数类型的表示方法
//方案一: 函数类型表达式 function type expression
//格式: (参数列表) => 返回值
type BarType = (num: number) => number
const bar: BarType = (aa: number): number => {
return 123
}
export { }
函数的表达方式
type BarType = (num1: number, num2: number) => number
function calc(calcFn: BarType) {
const num1 = 10
const num2 = 20
const res = calcFn(num1, num2)
console.log(res)
}
function add(num1: number, num2: number) {
return num1 + num2
}
calc(add)
//匿名函数
calc(function (num1, num2) {
return num1 * num2
})
export { }
函数的类型-调用签名
// 1.函数类型表达式
type BarType = (aa: number) => number
const bar: BarType = (val: number): number => {
return 123
}
// 2. 函数的调用签名
interface IBar {
name: string
age: number
//函数可以调用:函数调用签名
(num: number): number
}
const bar1: IBar = (val: number): number => {
return 123
}
bar1.name = 'pp'
bar1.age = 15
bar1(123)
export { }
函数的参数-可选参数
// y?是可选参数 类型为 number | undefined
function foo(x:number,y?:number){
return x
}
foo(10)
export {}
函数的参数-参数默认值
// 函数的参数可以有默认值
// 1. 有默认值的情况下,参数的类型注解可以省略
// 2. 有默认值的参数,是可以接收一个 undefined 的值
function foo(x:number, y=100){
console.log(x)
}
foo(10)
foo(20,undefined)
foo(10,30)
export{}
函数的参数-剩余参数
type val = (string | number)[]
function foo(...ages: val) {
console.log('----')
}
foo(123,'name')
export {}
函数的重载
// typescript 中函数的重载方法
// 1.先编写重载签名
//2 编写通用的函数实现
function add(x: number, y: number): number
function add(x: string, y: string): string
function add(x: any, y: any): any {
return x + y
}
add(10, 30)
add('a', 'r')
export { }
5.TypeScript面向对象
ts中类的基本使用
class Person{
name!: string
age!: number
constructor(name:string,age:number){
this.name = name
this,age = age
}
}
const p1 = new Person('张文',15)
console.log(p1.name,p1.age)
export {}
ts中类的成员修饰符
// public 默认
// private 只能在类中使用
// protected 只能在类中使用,子类也可访问
class Person {
protected name: string
private age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
private eating() {
console.log('吃东西', this.age)
}
}
class tt extends Person {
constructor(name: string, age: number) {
super(name, age)
}
yy() {
console.log(this.name)
}
}
export { }
ts中类的修饰符readonly
class Person{
readonly name:string
age:number
constructor(name:string,age:number){
this.name = name
this,age = age
}
}
const p = new Person('why',18)
console.log(p.name,p.age)
// p.name = '张三' readonly 只读属性,不能写入
p.age = 20
export{}
ts中类的setter 和 getters
class Person {
//私有属性:属性前面会使用_
// 为什么设置私有属性 ,限制不合理的赋值 ,比如年龄 不可能超过 200 ,就可以在set 拦截
private _age :number
constructor(age:number){
this._age = age
}
set age(newValue:number){
if(newValue > 0 && newValue < 200){
this._age = newValue
}else{
console.log('你的年龄挺特别!!!')
}
}
get age(){
return this._age
}
}
const p = new Person(20)
p.age = 30
console.log(p.age)
export {}
ts中类的参数属性的使用
class Person{
// 语法糖
constructor(public name:string){
this.name = name
}
}
const p = new Person('张')
p.name = '离'
export {}
ts中抽象类和方法的使用
//抽象类
abstract class Shape {
//getArea 方法只有声明,没有实现体
// 实现体让子类自己实现
// 可以将 getArea 方法定义为抽象方法:方法的前面加 abstract
// 抽象方法必须出现在抽象类中,类前面也要加 abstract
// 抽象类 不能 实例化
abstract getArea()
}
// 矩形面积
class jx extends Shape {
constructor(public x: number, public y: number) {
super()
}
getArea() {
return this.x * this.y
}
}
// 三角形面积
class sjx extends Shape {
constructor(public w: number, public h: number) {
super()
}
getArea() {
return (this.w * this.h) / 2
}
}
// 通用函数
function calcArea(shape: Shape) {
return shape.getArea()
}
calcArea(new jx(10, 20))
calcArea(new sjx(8, 10))
export { }
ts中的类型检测-鸭子类型
// TypeScript 对于类型检测的时候使用的鸭子类型
// 鸭子类型:如果一只鸟,走起来像鸭子,游起来像鸭子,看起来像鸭子,那么你可以认为它就是一只鸭子
// 鸭子类型,只关心属性和行为,不关心你具体是不是对应的类型
class Person{
constructor(public name:string, public age:number){}
}
function tt(p:Person){
console.log(p.name,p.age)
}
tt(new Person('zs',20))
tt({name:'44',age:10})
export{}
ts对象类型的修饰符
type info = {
name: string
age?: number
}
interface Ikun {
chang: string
tao: string
readonly rap: string
}
// ? 可选 readonly 只读
let t: info = {
name: '张三李四'
}
let t1: Ikun = {
chang: '情人',
tao: '练习两年半',
rap:'你干嘛!哎哟'
}
export { }
ts中的接口继承特性
interface XHZ {
name: string
age: number
}
//接口的继承
interface Ikun extends XHZ {
slogan: string
}
let ikun: Ikun = {
name: '蔡徐坤',
age: 18,
slogan: '你干嘛!哎哟'
}
export { }
ts中的接口的类实现过程
interface IKun {
name: string
age: number
playBasketball: () => void
}
interface IRun {
fn: () => void
}
class Person implements IKun, IRun {
name: string
age: number
playBasketball() {}
fn() {}
}
let IKun2 = new Person
console.log(IKun2.name, IKun2.age, IKun2.playBasketball, IKun2.fn)
export { }
ts严格字面量赋值检测
interface Iperson {
name:string
age:number
}
//奇怪的现象一
const obj = {
name:'言念',
age:18,
height:1.88
}
let info:Iperson = obj
//奇怪的现象二
function tt(val:Iperson){
}
tt(obj)
// 解释现象
// 第一次创建的对象字面量,称之为fresh (新鲜的)
// 对于新鲜的字面量,会进行严格的类型检测,必须完全满足类型的要求(不能有多余的属性)
export{}
ts中的枚举类型
// 枚举默认值为0,向上递增,可以赋值
enum tt {
LEFT,
RIGHT
}
enum ttt {
LEFT = 'LEFT',
RIGHT = 'RIGHT'
}
let s: tt = tt.LEFT
function ss(t: ttt) {
if (t === 'LEFT') {
console.log('-------')
}
}
ss(ttt.LEFT)
export { }
6.TypeScript泛型编程
泛型-类型的参数化
// 固定写死类型
function foo(name:string,age:number){
}
foo('陆青',18)
// 泛型
function IKun<Type>(val:Type):Type{
return val
}
// 完整写法
let t1 = IKun<number>(111)
let t2 = IKun<string>('陆青')
let t3 = IKun<{name:string}>({name:'陆青'})
// 简写
let t4 = IKun(111)
let t5 = IKun('陆青')
let t6 = IKun({name:'陆青'})
export{}
泛型-传入多个类型
function Ikun<Type, Element>(val1: Type, val2: Element) {
return '你干嘛!哎哟'
}
Ikun(10, 20)
Ikun(10, '陆青')
Ikun(10, [1, 2, 3])
export {}
泛型-泛型接口的使用
// 泛型可以赋值默认
type IKun<T = string> = {
name:T,
age:number,
love:T
}
let kunkun:IKun = {
name:'陆青',
age:20,
love:'打篮球'
}
let huang:IKun<number> = {
name: 10,
age:20,
love:11
}
export {}
泛型-泛型类的使用
class Point<T = number>{
x: T
y: T
constructor(x: T, y: T) {
this.x = x
this.y = y
}
}
const p1 = new Point(1, 3)
const p2 = new Point<string>('1', '3')
export { }
泛型-泛型约束使用
interface ILength {
length: number
}
// getLength 没必要使用泛型
function getLength(arg: ILength) {
return arg.length
}
const p1 = getLength('123')
const p2 = getLength(['q', 'w'])
const p3 = getLength({ name: 'rr', age: 10 })
// 获取传入的内容,这个内容必须有length属性
// Type 相当于是一个变量,用于记录本次调用的类型,所以整个函数的执行周期中,一直保留着参数的类型
function getInfo<Type extends ILength>(args: Type) {
return args
}
const p4 = getInfo('123')
const p5 = getInfo(['q', 'w'])
const p6 = getInfo({ name: 'rr', age: 10 })
export { }
泛型-泛型参数的使用约束
//传入key的类型
function getObjectProperty<O, K extends keyof O>(obj: O, key: K) {
return obj[key]
}
// K extends keyof O 等同于 K extends "name"|"age"|"love" keyof获取 info所有key
const info = {
name: '荒',
age: 24,
love: '最爱吃兽奶'
}
const name = getObjectProperty(info, 'name')
export { }
7.封装axios
创建一级文件夹 service ,二级文件夹config,request
service > index.ts
import { BASE_URL, TIME_OUT } from './config'
import HYRequest from './request'
const hyRequest = new HYRequest({
baseURL: BASE_URL,
timeout: TIME_OUT
})
export default hyRequest
config > index.ts
// 1 手动切换,有风险
// export const BASE_URL = 'xxxxxx'
// export const BASE_URL = 'xxxxxx'
// 2 vite默认提供的环境变量
// import.meta.env.MODE
console.log(import.meta.env.DEV) // 是否开发环境
console.log(import.meta.env.PROD) // 是否生产环境
console.log(import.meta.env.SSR) // 是否服务器渲染
// 3 新建.env 文件, 可以建多个(列如:.env.development(开发读取) .env.production(生产读取),命名使用VITE_ XXX,加上 .local表示不会提交到git
let BASE_URL = ''
if (import.meta.env.DEV) {
BASE_URL = 'xxxxxx'
} else {
BASE_URL = 'xxxxx'
}
console.log(BASE_URL)
export { BASE_URL }
export const TIME_OUT = 10000
request > index.ts
import axios from 'axios'
import type { AxiosInstance } from 'axios'
import type { HYRequestConfig } from './type'
class HYRequest {
instance: AxiosInstance
// request 实例 => axios 实例
constructor(config: HYRequestConfig) {
this.instance = axios.create(config)
//每个instance 都添加拦截器
this.instance.interceptors.request.use(
(config) => {
console.log('全局请求成功拦截')
return config
},
(err) => {
console.log('全局请求失败拦截')
return err
}
)
this.instance.interceptors.response.use(
(res) => {
console.log('全局响应成功拦截')
return res
},
(err) => {
console.log('全局响应失败的拦截')
return err
}
)
this.instance.interceptors.request.use(
config.interceptors?.requestSuccessFn,
config.interceptors?.requestFailureFn
)
this.instance.interceptors.response.use(
config.interceptors?.responseSuccessFn,
config.interceptors?.responseFailureFn
)
}
//封装网络请求方法
request<T = any>(config: HYRequestConfig<T>) {
if (config.interceptors?.requestSuccessFn) {
config = config.interceptors.requestSuccessFn(config)
}
return new Promise<T>((resolve, reject) => {
this.instance
.request<any, T>(config)
.then((res) => {
if (config.interceptors?.responseSuccessFn) {
res = config.interceptors.responseSuccessFn(res)
}
resolve(res)
})
.catch((err) => {
reject(err)
})
})
}
get<T = any>(config: HYRequestConfig<T>) {
return this.request({ ...config, method: 'GET' })
}
post<T = any>(config: HYRequestConfig<T>) {
return this.request({ ...config, method: 'POST' })
}
delete<T = any>(config: HYRequestConfig<T>) {
return this.request({ ...config, method: 'DELETE' })
}
patch<T = any>(config: HYRequestConfig<T>) {
return this.request({ ...config, method: 'PATCH' })
}
}
export default HYRequest
request > type.ts
import type { AxiosRequestConfig, AxiosResponse } from 'axios'
export interface HYInterceptors<T = AxiosResponse> {
requestSuccessFn: (config: AxiosRequestConfig) => AxiosRequestConfig
requestFailureFn: (err: any) => any
responseSuccessFn: (config: T) => T
responseFailureFn: (err: any) => any
}
export interface HYRequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
interceptors?: HYInterceptors<T>
}
在.vue 页面使用
<script lang="ts" setup>
import HYRequest from '../../service'
HYRequest.get({
url: '/home/multidata'
}).then((res) => {
console.log(res)
})
</script>
效果