MENU

TypeScript 学习笔寄

2023-02-10 • 笔记

原始类型

TypeScript 拥有 JavaScript 的数据类型并且新增了更多类型
JavaScript 类型:numberstringbooleannullundefined
TypeScript 类型:anytypeinterface

any 类型

any 是逃避 TypeScript 的类型检查,可以赋任何值,但是因此可能会导致 BUG 应该尽量避免使用
在已声明未赋值时就会自动推断为 any


类型注解

let age:number = 18 它的 :number 就是类型注解,它约定了这是什么类型的变量只能给它赋值这个类型,给它提供了约束让代码更清晰


数组类型

TypeScript 中数组类型有两种写法 类型[]Array<类型> 一般使用 类型[]

// 使用 类型[]
let listA: number[] = [1,1,4,5]
// 使用 Array<类型>
let listB: Array<string> = ['您','您','您']

联合类型

定义一个可以接收多个类型的类型注释

// 对于数组
let nyaList: (number | string | boolean)[] = [114514, '您您您', true]
let neko: Array<number | boolean> = [191919, false]

// 对于简单数据类型
let timer: number | null = null

// 对于字面量
let gender: '男' | '女' | '非二元' = '非二元'

类型别名和接口

在同一类型多次使用并且写法复杂时使用

类型别名 type 支持所有的类型,而接口 interface 只支持对象类型
类型别名 type 不允许重复命名,而接口 interface 重复命名会合并
类型别名 type 不能重新打开(向内定义新的对象注解)接口 interface 可以通过两个相同命名合并实现

类型别名

类型别名,使用大驼峰命名,类似于变量,使用它和类型注释的写法一样 type 别名 = 类型

// 数组类型别名
type CusArr = (number | string) []
let list: CusArr = [19, '您']

// 对象类型别名
type AddFnType = (num1: number, num2: number) => number
const addFn2 : AddFnType = (num1, num2) => {
  return num1 + num2
}

类型别名交叉

当你一个新的类型别名 B 与一个类型别名 A 拥有相同的属性,可以使用类型别名交叉获取上个类型别名 A 的属性注解,实现的效果类似于 interfaceextends

type Point2D = {
  x: number,
  y: number
}
// 从 Point2D 交叉
type Point3D = Point2D & {
  z: number
}
const p: Point3D = {
  x: 100,
  y: 100,
  z: 100
}

接口

接口和类型别名类似,也使用大驼峰命名,接口只能用于定义对象的类型注解,使用方法与类型别名相同,语法 interface 名称 {}

interface Person {
  name: string,
  age: number,
  say: () => void
}
let p: Person = {
  name: 'PinocoP',
  age: 28,
  say() {
    console.log('nai');
  }
}

接口继承

当你一个新的类型别名 B 与一个类型别名 A 拥有相同的属性,就可以使用 extends 进行接口继承,语法 interface 名称 extends 需要继承的接口名称 {}

interface Person {
  name: string,
  age: number,
  say(): void
}
interface Stu extends Person {
  score: number
}
const nin: Stu = {
  name: 'JuziYou',
  age: 18,
  score: 69,
  say() {
    console.log('您');
  }
}

函数类型

函数声明

function 函数名(形参: 类型):返回值类型{
  ...
}

函数表达式

const 函数名 = (形参:类型):返回值类型 => {}

函数返回值

如果函数没有返回值就是空 voidvoid 类名注解可以省略
在 JavaScript 中如果没有 return 返回值是 undefined 而在 TypeScript 中 voidundefined 并不相同,如果在返回值类型定义了 undefined 那么就必须 return undefined

可选参数

一个形参在可以传也可以不传的时候那就可以设置可选参数,在类型注释 ':' 前添加一个 '?' 即可,不传的情况下它的值是 undefined,可选参数不可以设置初始值,必传参数不能写在可选参数之后

// 函数声明
function 函数名(形参?: 类型, 形参:类型):返回值类型{
  ...
}

// 函数表达式
const 函数名 = (形参?:类型, 形参:类型):返回值类型 => {}

对象类型

对象类型有空对象和有属性的对象以及有属性和方法的对象(废话)
定义属性注解和可选属性和之前的那些方法差不多
对象类型在多个属性注解之间可以使用 , 也可以使用 ; 甚至可以直接换行

// 空对象
let person1 = {} = {}

// 有 属性 的对象
let person2: { name: string} = {
  name: 'juziyou'
}

// 有属性有方法的对象
let person3: { name: string, say(): void} = {
  name: 'JuziYou',
  say(){}
}
// 在多个对象属性间可以用 `,` 也可以用 `;` 甚至可以直接换行
let person4: {
  name: string;
  age: number
} = {
  name: 'JuziYou',
  age: 18
}
let person5: {
  name: string
  age: number
} = {
  name: 'JuziYou',
  age: 18
}

// 给箭头函数定义
let person6: {
  name: string,
  say: () => void
} = {
  name: 'JuziYou',
  say: () => console.log('喵')
  
}

// 接收一个多属性可选
// 列如 axios 的 config 中接收 url 和 method 其 method 可选
const axios = (config: {url: string, method?:string}) => {}

// 使用类型别名
type Config = (config: {url: string, method?:string}) => {}
const axios2 = (config: Config) => {}

类型推断

在 TypeScript 中存在类型推断机制,在没有指定类型的情况下会自动推断类型

// let num: number
let num = 114514

// fn(num1: number, num2:number): number
function fn(num1: number, num2:number) {
  return num1 + num2
}

字面量

在 TypeScript 中 let 了一个字面量相当于 const了一个常量如 let nya: 'JuziYou' = 'JuziYou' 那么它就只能赋值为 'JuziYou'let 的自动推断是赋予值的数据类型、const 的自动推断是赋予的值

// let neko: string
let neko = 'JuziYou'

// const nya: 'JuziYou'
const nya = 'JuziYou'

断言

当明确的知道数据类型就可以使用断言,as 断言它跟的类型是一个更加具体的类型

// 假设此时 #nya 是一个 <a> 标签
const link = document.getElementById('nya') as HTMLAnchorElement

非空断言

. 前面加 ! 来添加非空断言,告诉他一定有某个属性,比如 nya!.neko 就是告诉 TypeScript 在 nya 下一定有 neko


泛型

泛型是定义不确定是什么类型,使用的时候才知道,提高服用性和灵活性,一般使用大驼峰命名

泛型别名

在定义的别名后加上 <泛型参数>就是接收泛型,然后在下面的类型注解使用 泛型参数,然后在使用时在类型注解的类型别名后加上 <类型参数> 即可使用

type User = {
  name: string,
  age: number
}

type Goods = {
  id: number,
  name: string
}

type Data<Type> = {
  message: string,
  code: number,
  data: Type
}

// 假设是请求到用户信息
let user: Data<User> = {
  message: 'Okay',
  code: 200,
  data: {
    name: 'Mikan',
    age: 18
  }
}

// 假设是请求到商品信息
let goods: Data<Goods> = {
  message: 'Okay',
  code: 200,
  data: {
    name: '大猫猫抓板',
    id: 0
  }
}

泛型接口

在定义的接口名后加上 <泛型参数>就是接收泛型,然后在下面的类型注解使用 泛型参数,然后在使用时在类型注解的接口名后加上 <类型参数> 即可使用

interface IDs<Type> {
  id: ()=> Type,
  ids: ()=> Type[]
}

// 假设获取一个 ID 列表
const idList: IDs<number> = {
  id(){
    return 114514
  },
  ids(){
    return [19,1919,191919]
  }
}

泛型函数

在定义的函数名后加上<泛型参数>就是接收泛型,然后可以使用 泛型参数 在形参和返回值的类型注释中

function newToken<Type>(token: Type): Type{
  return token
}
let token: number = 191919
token = newToken<number>(114514)

泛型约束

利用了接口 interfaceextends 接口的继承原理,来约束传入的类型

例如必须有 length

function getId<T extends {length: number}> (id:T):T {}

自定义类型声明

如果多个 .ts 文件都要用到同个类型,这时就可以创建 .d.ts 来共享该类型

全局

直接在 .d.ts 中不使用 export 按需暴露的将会生成全局自定义类型
如:在 index.d.ts

interface Goods {
  name: string,
  price: number,
  brand: string
}

局部引入

TypeScript 类型也可以使用 importexport 来实现按需导入和导出功能,只需要再使用该 .d.ts.ts 文件中 import 导入即可使用

export interface Goods {
  name: string,
  price: number,
  brand: string
}
import {Goods} from './ts/index'
}
const goods:Goods = {
  name: '猫猫猫抓板',
  price: 114514,
  brand: '猫猫'
}

给 JS 提供类型

在导入 .js 文件时,TypeScript 会自动加载与 .js 同名的 .d.ts 文件,提供类型声明
declare 用于类型声明,为其他地方(比如,.js 文件)已存在的变量声明类型,而不是创建一个新的变量,在 interfacetype 时不用加 declare

/util/index.d.ts

export declare const sum : (numA: number, numB:number) => number

interface Student {
  name: string,
  score: number
}

export declare const say: (stu: Student) => void

/util/index.js

export const sum = (numA, numB) => numA + numB
export const say = (stu) => console.log(stu.name, stu.score);

index:

import { sum, say } from './util'
sum(1,2)
say({name: 'Nya', score: 66})

在 Vue 组合式 API 中使用

Vue官方文档

Props

基本使用

使用 defineProps<泛型>() 来来定义接收的数据类型

示例:

const props = defineProps<{money: number}>()

定义默认值

如果定义默认值需要使用 withDefaults 它第一个值接收一个函数写defineProps,第二个值写一个对象里面写接收的默认值

示例:

// const props = withDefaults(defineProps< 泛型 >(), {
//   接收值: 默认值
// })

const props = withDefaults(defineProps<{age: number, uname?: string}>(), {
uname: 'JuziYou'
})

语法糖 ⚗️

withDefaults 看起来比较复杂,所以可以使用响应式语法糖,目前还是实验性功能所以需要显式启用它,然后通过 const {接收值} = defineProps<泛型>() 使用

示例:

const {age, uname = 'JuziYou'} = defineProps<{
  age: number,
  uname?: string
}>()

Emit

不知道怎么解释就直接写上算了(摆)

子组件:
eventpayload 是自己定义的参数名 event 是触发事件的自定义事件名 payload 是传参

const emit = defineEmits<{
  (event: 'changeNya', payload:number):void
}>()

父组件:

const changeNyaFn = (nya: number) => {
  console.log('Nya', nya);
}

ref

通过 ref<泛型>(需要响应式的数据) 定义 ref

简单数据类型

定义简单数据类型 ref<泛型>(需要响应式的数据) 定义 ref

示例:

const uname = ref<string>('JuziYou')

自动推导

一般情况下 TypeScript 会自动推导它的数据类型所以可以 ref(需要响应式的数据) 这样写

示例:

// const uname: Ref<string>
const uname = ref('JuziYou')

复杂数据类型

如一个泛型为 Person 类型的数组

type Person = {
  id: number,
  uname: string,
  address: string
}
const personList = ref<Person[]>([])
personList.value.push({
  id: 1,
  uname: 'JuziYou',
  address: 'Nya'
})

reactive

使用 reactive 时推荐直接给变量指定类型

type Car = {
  brand: string,
  price: number,
  color?: string
}

const car:Car = reactive({
  brand: '橘子',
  price: 114514
})
car.color = "橘色"

computed

computed 一般会自动推导数据类型,也可以使用泛型指定
自动推导:

const nyaNum = ref(18)
// computed<number>
const nyaCount = computed(()=> nyaNum.value ** 2)

泛型指定:

const nyaNum = ref(18)
const nyaCount = computed<number>(()=> nyaNum.value ** 2)

事件

事件对象使用 Event 类型注释,通过类型断言来解决可能是个 null 的问题
如:获取 <input type="text" @change="changFn"> 的 value

const changFn = (event: Event) => {
  // 使用类型断言,断言 even.target 是一个 input
  console.log((event.target as HTMLInputElement).value)
  
}

ref 获取 DOM

const ref的值 = ref<类型 | null>(null) 来获取对应的 DOM
如:获取 ipt 再载入后默认聚焦

<script setup lang="ts">
const ipt = ref<HTMLInputElement | null>(null)
onMounted(()=>{
  ipt.value?.focus()
})
</script>

<template>
  <input type="text" ref="ipt" value="Nya">
</template>