TS学习笔记

一,TS中的八种基础类型

// 字符串
let str: string = 'hello world'
// 数字类型
let num: number = 123
// 布尔类型
let bool: boolean = true
// null
let n: null = null
// undefined
let u: undefined
// 对象类型
let obj: object = {}
// bigint
let big: bigint = 100n
//symbol
let sym: symbol = Symbol()

 二,void,never,any,unknown类型

1,void类型表示没有任何类型,不能直接赋值

let a: void
let b: number = a //报错 void不能赋值给number

这种没有什么意义 如果一个函数没有返回值 可以给他赋值为void 

function fn(): void {
  console.log('测试666')
}

2, never类型表示永不存在的值的类型

// 抛出异常
function error(): never {
  throw new Error('error')
}
// 死循环
function loop(): never {
  while (true) {
    console.log('死循环')
  }
}

 3,any类型表示可以是任何类型(无敌 anyScript)

let num: number = 10
num = '123' //报错 不能将类型“string”分配给类型“number”

// 使用any之后
let num2: any = 10
num2 = '123' //可以

num2.a() //不报错

不知道什么类型的时候 想省心就能使用any

4,unknown类型与any类型差不多,所有类型都可以分配给unknown,但是把unknown类型赋值给其他类型会报错

// unknown可以接收任何类型
let num: number = 10
let un: unknown = num

num = un //报错 不能将类型“unknown”分配给类型“number”

三,数组跟函数类型定义

1,数组类型的定义

// 数组类型定义
const list1: number[] = [1, 2, 3, 4]
const list2: Array<number> = [1, 2, 3, 4]
const list3: [number, string, boolean] = [1, '2', true]
const list4: [{ name: string; age: number }] = [{ name: 'admin', age: 18 }]
const list5: Array<{ name: string; age: number }> = [{ name: 'admin', age: 18 }]
const list6: { name: string; age: number }[] = [{ name: 'admin', age: 18 }]
const list7 = [{ name: 'admin', age: 18 }] as { name: string; age: number }[]
interface ObjType {
  name: string
  age: number
}
const list8: ObjType[] = [{ name: 'admin', age: 18 }]
const list9: Array<ObjType> = [{ name: 'admin', age: 18 }]

2,函数类型定义

// 函数类型定义
const fn1 = (a: number, b: number): number => {
  return a + b
}
const fn: (a: number, b: number) => number = (a, b) => {
  return a + b
}

四,元组

数组有时候是同类型的值 但是有时候也会存在多种类型值的情况 定义数组不同类型称之为元组

// 数组同一类型
let arr: number[] = [1, 2, 3]
// 数组不同类型 
// 元组定义
const arr2: [number, string, number] = [1, '2', 3]

五,可选类型

// 可选类型 ?表示可有可没有
const arr1: [number, string, number?] = [1, '2', 3] //不报错
const arr2: [number, string, number?] = [1, '2'] //不报错

六,交叉类型

// 交叉类型
let a: string | number = 1
a = '1' //不报错
a = 1 //不报错
a = true //报错
// 交叉类型
interface User {
  name: string
  age: number
}
interface Sex {
  sex: string
}
// 相当于继承
let user: User & Sex = {
  name: 'zhangsan',
  age: 18,
  sex: '男'
}

七,对象类型与索引签名

// 声明一个用户类型
interface User {
  name: string
  age: number
}

// 第一个用户 不报错
const user1: User = {
  name: '张三',
  age: 18
}

// 第二个用户 报错缺少age属性
const user2: User = {
  name: '李四'
}

因为在User类型中声明了age 当我们不确定是否存在age的时候可以通过?可选来控制 

// 声明一个用户类型
interface User {
  name: string
  age?: number //可选类型
}

// 第一个用户 不报错
const user1: User = {
  name: '张三',
  age: 18
}

// 第二个用户 不报错
const user2: User = {
  name: '李四'
}

还有一种情况,在实际开发中不确定后端返回多少字段,比如 

// 声明一个用户类型
interface User {
  name: string
  age?: number //可选类型
}

// 第一个用户 不报错
const user1: User = {
  name: '张三',
  age: 18,
  address: '北京', //报错
  desc: '我是一个描述' //报错
  //很多不缺定属性....
}

这时候我们可以通过索引签名来解决问题

// 声明一个用户类型
interface User {
  name: string
  age?: number //可选类型
  // 索引签名
  [key: string]: any
}

// 第一个用户 不报错
const user1: User = {
  name: '张三',
  age: 18,
  address: '北京', //索引签名不报错
  desc: '我是一个描述' //索引签名不报错
  //很多不缺定属性....
}

八,类型断言 

类型断言可以用来手动指定一个值的类型,即允许变量从一种类型更改为另一种类型。

interface Obj {
  name: string
}
// 让obj被断言成Obj类型
let obj = {} as Obj
// 这时obj已经被断言成Obj类型,所以name属性是存在的
obj.name

九,函数重载

函数重载是指函数约束传入不同的参数,返回不同类型的数据,而且可以清晰的知道自己传入的参数以及不同的结果

// 函数重载
// 首先定义了一个函数
// 接受三个参数 第一个参数是必须选择的 后续几个参数可选
function fn(n1: number, n2?: number, n3?: number, n4?: number) {
  console.log(n1, n2, n3, n4)
}

// 以下几种调用方式都是正确的
fn(1)
fn(1, 2)
fn(1, 2, 3)
fn(1, 2, 3, 4)

 现在我们希望能传四个参数但是不可以传递三个参数 我们就可以使用函数重载 

// 函数重载
// 首先定义了一个函数
// 接受三个参数 第一个参数是必须选择的 后续几个参数可选

// 函数重载
function fn(n1: number): any //传递一个参数
function fn(n1: number, n2: number): any //传递二个参数
function fn(n1: number, n2: number, n3: number, n4: number): any //传递四个参数个参数
function fn(n1: number, n2?: number, n3?: number, n4?: number) {
  console.log(n1, n2, n3, n4)
}

fn(1) //不报错
fn(1, 2) //不报错
fn(1, 2, 3) // 报错 没有需要 3 参数的重载,但存在需要 2 或 4 参数的重载。ts
fn(1, 2, 3, 4) //不报错

还可以用来实现传入类型返回类型限制

// 函数重载
// 需求 如果x是字符串y也必须是字符串
// 相反如果是number y也必须是number
function fn(x: string | number, y: string | number) {
  console.log(x, y)
}

fn('1', '2') //不报错
fn(1, 2) //不报错
fn(1, '2') //不报错

要怎么实现 

// 函数重载
// 需求 如果x是字符串y也必须是字符串
// 相反 如果是number y也必须是number
function fn(x: string, y: string): any
function fn(x: number, y: number): any
function fn(x: string | number, y: string | number) {
  console.log(x, y)
}

fn('1', '2') //不报错
fn(1, 2) //不报错
fn(1, '2') //报错 没有与此调用匹配的重载

 十,可调用注解

我们想为一个函数进行类型注解可以通过

type A = () => void
let a: A = () => {
  console.log('a')
}

可调用注解的写法

type A = {
  (): void
}
let a: A = () => {
  console.log('a')
}

可以配合函数重载来限制类型重载的个数 

type Fn = {
  (x: string, y: string): any
  (x: number, y: number): any
}
function fn(x: string, y: string): any
function fn(x: number, y: number): any
function fn(x: string | number, y: string | number) {}

let fn2: Fn = fn

定义了两个类型重载 这时候页面是正常的 如果我们删除一个就会报错 

type Fn = {
  (x: string, y: string): any
  (x: number, y: number): any
}
function fn(x: string, y: string): any
function fn(x: string | number, y: string | number) {}

let fn2: Fn = fn //报错 

也可以给函数注解属性

type Fn = {
  (x: string, y: string): any
  (x: number, y: number): any
}
function fn(x: string, y: string): any
function fn(x: number, y: number): any
function fn(x: string | number, y: string | number) {}

let fn2: Fn = fn

fn2.username //报错类型“Fn”上不存在属性“username”

为Fn注解添加username属性注解 


type Fn = {
  (x: string, y: string): any
  (x: number, y: number): any
  username?: string
}
function fn(x: string, y: string): any
function fn(x: number, y: number): any
function fn(x: string | number, y: string | number) {}

let fn2: Fn = fn

fn2.username //不报错

可调用注解配合函数可以实现多个函数注解 普通的函数写法只能写一个 

// 只能声明一个
type Fn = (x: string, y: string) => any
// 声明多个
type Fn = {
  (x: string, y: string): any
  (x: number, y: number): any
  username?: string
}

十一,枚举 

在 TypeScript 中,枚举(Enum)是一种数据类型,用于定义一组命名的常量值。枚举可以帮助开发者更清晰地表达意图,提高代码的可读性和可维护性。

基本语法

enum Direction {
  Up,
  Down,
  Left,
  Right
}
// Direction 枚举定义了四个常量值:Up、Down、Left、Right。
// 默认情况下,枚举成员的值是从 0 开始递增的。
// 因此,Direction.Up 的值为 0,Direction.Down 的值为 1,以此类推。
// 在数字情况下我们是可以通过 Direction[1]的形式来反向获取枚举成员的名称的。

当我们修改了值为string之后 递增就会失效

enum Direction {
  Up = 'up',
  Down, //报错 枚举成员必须具有初始化表达式
  Left, //报错 枚举成员必须具有初始化表达式
  Right //报错 枚举成员必须具有初始化表达式
}
enum Direction {
  Up = 'up',
  Down = 'down',
  Left = 'left',
  Right = 'right'
}

常量枚举 

const enum Direction {
  Up = 'up',
  Down = 'down',
  Left = 'left',
  Right = 'right'
}

代码编译后 枚举会被直接替换成常量 代码显示更整洁

十二,接口跟类型别名的区别

相同点

// 接口与类型别名都可以用来定义对象类型
// 都可以使用索引签名
// 也可以声明类型注解
// 同样支持可选参数 以及只读属性
interface User {
  name: string
  age?: number
  [index: string]: any
  (): void
}

type User2 = {
  readonly name: string // 只读属性
  age?: number // 可选参数
  [index: string]: any
  (): void
}

区别 

// 区别1 接口只能声明对象类型{} 但是别名可以指定基础类型
type Str = string;
interface Str2 = string //报错 接口后面只能跟{}

// 区别2 接口具备合并功能 别名不能合并
interface IName {
  name: string;
}
interface IName {
  age: number;
} 
// 两个相同类型名称 合并 现在的Iname 就是 {name:string,age:number}
const name: IName = {
  name: 'zhangsan',
  age: 18
}

// 区别3 接口可以继承 别名不能继承
interface A {
  name: string;
}
interface B extends A {
  age: number;
}
// B继承了A 所以可以拥有A的属性
const b: B = {
  name: 'zhangsan',
  age: 18
}

// 区别4 别名具备映射功能 接口不能
type Z = {
  [P in 'name' | 'age']:string
}
// type Z = {
//   name: string;
//   age: string;
// }

 十三,字面量类型与keyof

// 字面量类型
type Status = 'loading' | 'success' | 'success'
// 举例
let status: Status = 'loading'
let status2: Status = 'success'
let status3: Status = 'success'
let status4: Status = '6' // 报错 不能将类型“"6"”分配给类型“Status”

// keyof 关键字
interface User {
  name: string
  age: number
}
type UserKeys = keyof User // "name" | "age"
let userKey: UserKeys = 'name'
let userKey2: UserKeys = 'age'
let userKey3: UserKeys = 'sex' // 报错 不能将类型“"sex"”分配给类型“UserKeys”

十四,类型保护

案列一

// 案列一
const fn = (n: string | number) => {
  console.log(n.length) // 报错  类型“number”上不存在属性“length”
}

解决方法1 可以通过断言来欺骗编译器 这就是一个字符串

const fn = (n: string | number) => {
  console.log((n as string).length) // 问题解决 断言之后就相当于告诉编译器这是String
}

 但是不推荐因为 我们在接收参数的时候是允许传入数字的

解决方法2 使用typeof增加一层判断

const fn = (n: string | number) => {
  if (typeof n === 'string') {
    console.log(n.length) // 进入到这个判断里的只能是字符串 问题解决
  }
}

案例二

// 案列二
const fn = (obj: { name: string } | { age: number }) => {
  console.log(obj.name) // 报错 类型“{ name: string; } | { age: number; }”上不存在属性“name”
}

因为可能是只传入了一个age 

解决方法 我们一样可以增加一层判断来进行类型保护

const fn = (obj: { name: string } | { age: number }) => {
  if ('name' in obj) {
    console.log(obj.name) // 问题解决 如果name in obj 代表传入的就是第一个类型
  }
}

案列三 

// 案例三
class Dog {
  name = '旺财'
}

class User {
  phone = '123456'
}

// 参数接收一个类
const fn = (c: Dog | User) => {
  console.log(c.phone) // 错误 因为可能传入的是狗的类 所以没有电话
}

解决办法 使用instanceof 来进行类型保护

// 类型保护
// 案例三
class Dog {
  name = '旺财'
}

class User {
  phone = '123456'
}

// 参数接收一个类
const fn = (c: Dog | User) => {
  if (c instanceof Dog) {
    console.log(c.name)
  } else {
    console.log(c.phone)
  }
}

案例四 

封装方法类型保护失效问题

// 案例四
// 封装函数
const typeFn = (n: string | number) => {
  return typeof n === 'string'
}
const fn = (n: number | string) => {
  if (typeFn(n)) {
    console.log(n.length) // 报错 类型“string | number”上不存在属性“length”。 类型保护失效
  }
}

 解决办法使用 is类型谓词 来进行类型保护

// 封装函数 使用is类型谓词
const typeFn = (n: string | number): n is string => {
  return typeof n === 'string'
}
const fn = (n: number | string) => {
  if (typeFn(n)) {
    console.log(n.length) // 问题解决
  }
}

 十五,泛型

定义函数接口或者是类的时候 未指定参数类型 希望在运行时传入才能确定

基本使用

// 泛型
// 定义一个基本泛型 T是参数名称随便起
type A<T> = T

// 传递了string 就是 string类型
const a: A<string> = '123'
// 传递了number 就是 number类型
const b: A<number> = 123
// 传递了boolean 就是 boolean类型
const c: A<boolean> = true

如果不传

// 如果不传
const d: A = '123' // 报错 泛型类型“A”需要 1 个类型参数

可以给泛型定义默认值

// 可以传递默认值 T = string
type A<T = string> = T

// 传递了string 就是 string类型
const a: A<string> = '123'
// 传递了number 就是 number类型
const b: A<number> = 123
// 传递了boolean 就是 boolean类型
const c: A<boolean> = true

// 如果不传
const d: A = '123' // 问题解决 不传默认字符串

 函数使用

// 函数中使用
function fn<T>(n: T) {
  console.log(n)
}

fn<string>('123')
fn<number>('123') // 报错 应该传number

函数与接口进行结合 

// 函数与接口结合使用
// 定义一个接口 参数由泛型决定
interface A<T> {
  (n: T): void
  default?: T
}
// 当泛型传递为number时 default为number
let fn: A<number> = n => {
  console.log(n)
}
fn.default = 1
let fn2: A<string> = n => {
  console.log(n)
}
// 当泛型传递为String时 default为String 再赋值数值报错
fn2.default = 1 // 报错 不能将类型“number”分配给类型“string”

类中使用

// 在类中使用
class Dog<T> {
  name: T // 报错 属性“name”没有初始化表达式,且未在构造函数中明确赋值 
}

 类中需要给值赋予初始值 可以使用 ! 非空断言解决

// 在类中使用
class Dog<T> {
  name!: T // 解决
}
const dog = new Dog<string>()
dog.name = '小黑'
dog.name = 123 // 报错 因为泛型传递的是字符串

类继承 

// 在类中使用
class Dog<T> {
  name!: T
}
const dog = new Dog<string>()
// dog2继承dog1 泛型传递字符串
class Dog2 extends Dog<string> {
  age!: number
}
const dog2 = new Dog2()
dog2.name = '小黑'
dog2.age = 2
// 泛型传递字符串 赋值number 报错
dog2.name = 123 // 报错

 泛型约束

// 泛型约束
function fn<T>(n: T) {}
// 不传递泛型的时候 会自动根据传递的参数进行推导 所以传什么都不报错
fn(123) //不报错
fn('111') //不报错

进行约束 

// 泛型约束

// 创建一个别名
type A = string
// extends进行泛型约束
function fn<T extends A>(n: T) {}
fn(123) //报错 约束了类型 为字符串
fn('111') //不报错
// 泛型约束

// 也可以要求参数是一个包含length属性的类型
type A = {
  length: number
}
// extends进行泛型约束
function fn<T extends A>(n: T) {}
fn([]) //不报错
fn('111') //不报错
fn(1) //报错 number不具备length

十六,类型兼容性 

// 类型兼容性
let a: { name: string } = { name: 'admin' }
let b: { name: string; age: number } = { name: 'admin', age: 18 }

a = b // 赋值可以 b中包含a中属性,所以可以赋值
b = a // 报错,a中缺少b中属性,所以不能赋值

const fn = (a: { name: string }) => {}
fn({ name: 'admin', age: 18 }) // 报错

// 根据变量 类型兼容性
let c: { name: string; age: number } = { name: 'admin', age: 18 }
fn(c) // 不报错 

 还有很多东西 懒得记了. . . . . .

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值