typescript学习日记

「1.9W字总结」一份通俗易懂的 TS 教程,入门 + 实战! - 掘金

也可以看一下这个网址的内容

声明基本数据类型

// 声明基本数据类型

let str:string = 'abc' //声明一个字符串类型的变量
let num:number = 123 //声明一个数字类型的变量
let bool:Boolean = Boolean(1) //或者直接赋值true
function fn():void{
  // void 代表函数没有返回值 
  // 如果return 返回了值 则会提示错误信息
}

// 通过void 声明 undefined 和 null 
let u:void = undefined
let n:void = null

// 通过 undefined 和 null 声明 这两个可以穿插使用
let u:undefined = undefined
let n:null = null 

// 这个情况基本不会使用
let u:null = undefined
let n:undefined = null 

ts的顶级类型 any 和 unknow

// any 和 unknown 类型都是 typeScript 中的顶级类型
let anys:any = 'abc'
let anys:any = 123
let anys:any = true | false 
let anys:any = {}
let anys:any = []
let anys:any = Symbol()

// 使用 any 可以声明任意类型的数据


// unknow 和 any 用法上的一些区别

// unknow 类型 
let unknow:unknow = { a:1 }
unknow.a //unknow是不可以使用的
// any类型
let unknow:any = { a:1 }
unknow.a //any是可以使用的


// unknow 类型只可以当做父类 不可以当做子类
let str:unknow = 'abc'
let str1:string = 'bbb'
str1 = str // 这种用法会报错  类型只可以当做父类 不可以当做子类

// any 类型 可以当做父类 也可以当做子类
let str:any = 'abc'
let str1:string = 'bbb'
str1 = str // 这种用法不会报错

// 相对比来说 unknow 比 any 更加的安全
// unknow 类型不可以调用属性和方法
// any 类型可以调用属性和方法

// unknow 只可以赋值 unknow 类型和 any 类型

接口 Interface 和对象使用 接口

// 定义接口
Interface A {
  name:string, // 接口中定义一个字符类型的变量
  fn():void // 接口中定义一个没有返回值的函数
}
// 声明一个 A接口类型的 obj 对象  对象中的类型必须和接口中的类型保持一致
class obj:A = {
  name:'abc',
  fn:()=>{} // 没有返回值的函数
}


// 接口重名
interface A {
  name:string
}
interface A {
 age:number 
}

// 只读属性
interface Person {
    age:number
    name:string
    readonly id:number
}

// interface 描述函数类型
interface ISum {
   (x:number,y:number):number
}

const add:ISum = (num1, num2) => {
    return num1 + num2
}

// 当两个接口重名时  会自动合并 对象中的类型就必须同时满足重名接口中的所有类型
class obj:A = {
  name:'abc',
  age:12
}

接口继承

Interface A {
  name:string
}

// 接口 B 继承 接口 A 
Interface B extends A {
  age:number
}

// 接口继承通过 es6 中的 extends 实现接口继承 

// 在对象声明使用时  对象中的属性类型 既得满足 B 中定义的 同时也得满足 A 中定义的
class obj:B = {
  name:'abc',
  number:123
}

可选式操作符 联合类型 接口任意类型[propName:string]

// 当定义接口时  不确定是否需要一个变量时  可以使用可选式操作服 ?来进行类型定义
Interface A {
	name:string,
  age?:number
}
// 当不确定是否需要 age 这个属性是 可以通过 ? 来声明该属性的类型 这样 age 属性就是可传可不传

class obj:A = {
  	name:‘abc’,
    age:123 // 这个 age 属性就是可用可不用的  用和不用都不会报错
}

// 联合类型
Interface B {
  name:string,
  age:number,
  // school 当一个属性 可能会有两个或多个类型时 我们可以考虑使用联合类型
  // 联合类型用法
  school:string | number //代用 这个 school 属性可以是字符串类型 也可以是数字类型
}

// 任意类型
Interface C {
  name:string,
  age:number,
  // 当我们不知道在用的时候到底会有多少个属性时 可以用到任意类型 [propName:string] 关键字
  [propName:string]:string //后面是 string 类型时 整个接口中所有的类型必须都是string 类型 不然会报错
  [propName:string]:number //与上面 string 类型同理 接口中所有类型必须是 number 类型
  [propName:string]:number | string //代用这个接口中可以是 数字类型 或者 字符类型
  // [propName:string]:any //一般情况下 都是 any 类型
  [propName:string]:any //代表接口中可以有任意类型的属性 
}

只读属性 readonly

Interface A {
	readonly name:string
}

class obj:A = {
  name:'abc'
}

console.log(obj.name) // 只可以被读取

// error
obj.name = 'def' //被 readonly 定义的变量只可以读取 不可以修改

class Person {
    name: string
    constructor(name: string) {
        this.name = name
    }
    speak() {
        console.log(`${this.name} is speaking`)
    }
}

const p1 = new Person('lin')      // 新建实例  

p1.name                           // 访问属性和方法
p1.speak()

// 使用 extends 关键字实现继承,定义一个 Student 类继承自 Person 类。
class Student extends Person {
    study() {
        console.log(`${this.name} needs study`)
    }
}

const s1 = new Student('lin')

s1.study()
s1.speak() // 子类继承父类之后 也可以调用父类的方法

// super关键字
// 注意,上例中 Student 类没有定义自己的属性,可以不写 super ,
// 但是如果 Student 类有自己的属性,就要用到 super 关键字来把父类的属性继承过来。
// 比如,Student 类新增一个 grade(成绩) 属性,就要这么写:
class Student extends Person {
    grade: number
    constructor(name: string,grade:number) {
        super(name)
        this.grade = grade
    }
}
const s1 = new Student('lin', 100)

// 类的一些属性 public 、 private 、 protected 、 static
// public,公有的,一个类里默认所有的方法和属性都是 public。
// 比如上文中定义的 Person 类,其实是这样的:
class Person {
    public name: string
    public constructor(name: string) {
        this.name = name
    }
    public speak() {
        console.log(`${this.name} is speaking`)
    }
}
// public 可写可不写,不写默认也是 public

// private
// private,私有的,只属于这个类自己,它的实例和继承它的子类都访问不到。
// 将 Person 类的 name 属性改为 private。

class Person {
    private name: string
    public constructor(name: string) {
        this.name = name
    }
    public speak() {
        console.log(`${this.name} is speaking`)
    }
}


// protected
// protected 受保护的,继承它的子类可以访问,实例不能访问。
// 将 Person 类的 name 属性改为 protected。
class Person {
    protected name: string
    public constructor(name: string) {
        this.name = name
    }
    public speak() {
        console.log(`${this.name} is speaking`)
    }
}

// 实例访问 name 属性,会报错
let p1 = new Person('chen')
p1.name // 报错

// 子类可以访问
class Studeng extends Person {
    study() {
        console.log(`${this.name} needs study`)
    }
}


// static
// static 是静态属性,可以理解为是类上的一些常量,实例不能访问。
// 比如一个 Circle 类,圆周率是 3.14,可以直接定义一个静态属性。
class Circle {
    static pi = 3.14
    public radius: number
    public constructor(radius: number) {
        this.radius = radius
    }
    public calcLength() {
        return Circle.pi * this.radius * 2  // 计算周长,直接访问 Circle.pi
    }
}

// 实例访问,会报错:
let c1 = new Circle(10)

抽象类 ( 这个暂时还没有更新完 还有一些 有时间在更新 )

// TS 通过 public、private、protected 三个修饰符来增强了 JS 中的类。
// 其实 TS 还对 JS 扩展了一个新概念——抽象类。
// 所谓抽象类,是指只能被继承,但不能被实例化的类,就这么简单。
// 抽象类有两个特点:
// 抽象类不允许被实例化
// 抽象类中的抽象方法必须被子类实现
// 抽象类用一个 abstract 关键字来定义,我们通过两个例子来感受一下抽象类的两个特点。

// 抽象类不允许被实例化
abstract class Animal {}

const a = new Animal()

// 定义一个抽象类 Animal,初始化一个 Animal 的实例,直接报错,

// 抽象类中的抽象方法必须被子类实现
abstract class Animal {
    constructor(name:string) {
        this.name = name
    }
    public name: string
    public abstract sayHi():void
}

class Dog extends Animal {
    constructor(name:string) {
        super(name)
    }
}
// 定义一个 Dog 类,继承自 Animal 类,但是却没有实现 Animal 类上的抽象方法 sayHi,报错,
// 正确的用法如下,
abstract class Animal {
    constructor(name:string) {
        this.name = name
    }
    public name: string
    public abstract sayHi():void
}

class Dog extends Animal {
    constructor(name:string) {
        super(name)
    }
    public sayHi() {
        console.log('wang')
    }
}

数组类型

// 第一种定义数组的方法
let arr:number[] = [1,2,3] //定义一个数字类型的数组 这个数组中只可以有数字
let arr:string[] = ['啦啦啦','你是个麻瓜'] //定义一个字符类型的数组 这个数组中只可以有字符
let arr:any[] = [1,'aaa',true,[],{}] //定义一个任意类型的数组 这个数组中可以有任意类型

// 第二种定义数组的方法 数组泛型1
let arr:Array<number> = [1,2,3 //这也是声明的数字类型的数组 只可以有数字
let arr:Array<string> = ['啦啦啦','你是个麻瓜'] //字符型
let arr:Array<any> = [1,'aaa',true,[],{}] //任意类型
// 还有其他的类型也是在 <> 中写

// 多维数组声明
let arr:number[] = [] //一维数组
let arr1:number[][] = [ [] ] //二维数组
let arr2:number[][][] = [   [   []  ]     ]  //三维数组

// 泛型声明多维数组
let arr:Array<number> = [] //一维数组
let arr1:Array<Array<number>> = [  [] ] //二维数组


// 通过接口定义数组
Interface ArrNumber {
   [index:number] : number // 这个是定义数字类型的数组 
   // [index:number] : string // 字符类型的数组
   // [index:number] : any   // 任意类型的数组
}

let arr:ArrNumber[] = [ 1,2,3 ] //接口声明的是数字类型的 数组中就只可以有数字
 


联合类型 类型断言 交叉类型

// 联合类型 |
let a:number = 123 // 正常定义类型是数字类型 
// 当我们想要数字或者字符类型时  可以使用 联合类型
let b:string | number = 'aaa' // 这时 b 可以是字符类型 也可以是数字类型

// 定义了一个方法 想要返回一个布尔类型
// 此时 参数的类型可以时数字类型 也可以是布尔类型
function fn(type:number | boolean):boolean{
  return !!type // 双感叹号 强转 1-> !1 转为 false false->!false 转为true
}
// 调用方法  传递实参时我们可以传递数字也可以传递布尔 
fn(1)
fn(0)
fn(true)
fn(false)


// 类型断言 as  断言只能帮助我们欺骗编译器 但是改变不了最后的输出结果 
// 简单理解  就是只会让编译器不会飘红报错 但是 最后的结果不会有其他的改变
function fn(num:number | string):void{
  console.log(num.length) // 此时 数字是没有 length 属性的
  console.log((num as string).length) // 我们可以使用断言 我们的参数就是字符串
}
fn('12345') //结果是 5

function fn(num:number | string):void{
  console.log(num.length) // 此时 数字是没有 length 属性的
  console.log((num as string).length) // 我们可以使用断言 我们的参数就是字符串
}
fn(12345) //当我们传参数是数字时 打印的值就是undefined  断言只能帮我们欺骗编译器 但是结果改变


interface A {
  run:string
}
interface B {
  build:string
}

let fn (type:A | B):void => {
  console.log(type.run) //此时打印会报错 应为B接口上并没有run这个属性
  // 使用断言
  console.log((<A>type).run) //断言我们的type 是 A 类型的
  // 也可以使用 as 方法
  console.log((type as A).run) //断言我们的type 是 A 类型的
}


// 交叉类型 &
interface A {
  name:string,
  age:number
}

interface B {
  sex:string
}

// 交叉类型在使用时 类似于 extends 继承 也是必须满足两个类型的条件
// 既得满足 A 中的条件 也得满足 B 中的条件
function fn(zihao:A & B):void{
  console.log(zihao)
}

fn({
  name:'子豪',
  age:18,
  sex:'男'
})


内置对象

// 正则
let regexp:RegExp = /\w\d\s/ 
// 日期
let date:Date = new Date()
let date:Date = new Date().getTime() //这么赋值的话会报错 getTime 是数字类型
// erroe
let error:Error = new Error('错误')

// 还有很多 懒得写了  百度查一下看看吧


class类

// 声明class类
class Person {
  // 在这定义的必须在 constructor 中使用  
  name:string,
  age:number,
  sub:boolean
  constructor( name:string,age:number,sub:boolean ){
    // 在这里面不使用的话 可能会在上面未使用的变量名下飘红
    this.name = name
    this.age = age
    this.sub = sub
  }
}
new Person( '子豪',18,true )
 
// 类修饰符 public private protected static
public //内部外部都能访问到
private //私有变量只能在内部使用
protected //只能在内部和子类中使用
static //静态属性 也可以定义静态方法 static 定义的静态属性或方法 可以直接通过类名 Person 调用 
// 在静态方法中不可以通过this来调用类中定义的变量 只可以访问 static 定义的变量



implements //用来约束类型
interface A {
  run(type:boolean):boolean
}

interface B {
  dev():void
}

class H {
  name:string
  constructor(name){
    this.name = name
  }
}

// newPerson 继承 H 
// 通过 implements 定义类 的类型 有多个的话 , 隔开
class newPerson extends H implements A,B {
  run(type:boolean):boolean{
    return type
  }
}

类型推论 和 类型别名

// 当我们没有给变量 声明类型时  我们赋值的类型就是变量的类型
// 编译器会帮我们 根据 赋值的类型对我们的变量做类型推断
let str = 'abc' 

// 当我们已经赋值过字符类型时 变量会默认是个 string 类型 
// 此时在赋值数字 会报错 提示 str 是一个 字符类型的
str = 345

// 类型别名

// 通过 type 定义 s 为 string 类型
type s : string 
// s 使用 类型别名 定义为了 string 类型
let str:s = 'abc'


// 类型别名也支持联合类型
type s : string | number

// 可以赋值字符串 或者 数字
let str:s = 'abc'
let str:s = 123
// 当赋值不符合的类型时 会飘红报错
let str:s = true


// 函数式了类型别名
type fn()=>string

function fnc:fn()=> return 'abc'


// 值的类型别名 

type T: 'on' | 'off'

// 使用值的类型别名之后 只可以赋值 定义好的值
let str:T = 'on'
let str:T = 'off'

never类型

// never 类型 表示1不应该存在的类型或状态

// 这么定义类型 str 类型会是 never类型的 
// 编译器认为 即使 string 又是 number 类型 是一个不可能的事情 所以str 是 never 类型
let str:string & number 

// 函数 当函数内保抛出异常时  那函数就永远不可能有返回值 所以返回值是 never 类型
function fn(message:string):never{
  throw new Error(message)
}

function loop():never{
  // 函数内部是一个死循环的话 也是永远不会有返回值
  while(true){
    
  }
}

// 接口
interface A {
  type:'子豪'
}
interface B {
  type:'20'
}
// 联合类型
let All = A | B
function fn(val:All){
  switch(val.type){
      case: '子豪';
      	break;
      case: '20';
      	break;
      // default也称之为兜底逻辑 当case条件全部不满足时 会走兜里逻辑
    	default:
        const check:never = val;
      break;
  }
}

迭代器 [Symbol.iterator]()

let arr:Array<number> = [ 1,2,3 ]

let it:Iterator<number> = arr.[Symbol.interator]()

it.next() // 执行结果是一个对象 { value:数组中的值 , done:false }
// { value:数组中的值 , done:false }
// 对象中的 done 为true 时 代表数组执行完了 没有值了


// 迭代器测试
let arr:Aarray<number> = [ 1,2,3,4 ]

let set:Set<number> = new Set([ 1,2,3,4 ])

// type mapKeys:string | number
// Map()有两个参数 一个 key 一个value
let map:Map<mapKeys,mapKeys> = new Map()
map.set('1','王爷')
map.set('2','皇上')


function gen(erg:any){
  let it:Iterator<any> = erg[Symbol.iterator]()
  let next = { dnoe:false }

  while(!next.done){
    next = it.next()
    if(!next.donw){
      console.log(next)
    }
  }
}
gen(arr)
// 结果 
// { value:1 , done:false }
// { value:2 , done:false }
// { value:3 , done:false }
// { value:4 , done:false }

gen(set)
// 结果 
// { value:1 , done:false }
// { value:2 , done:false }
// { value:3 , done:false }
// { value:4 , done:false }

gen(map)
// 结果 
// { value:['1',‘王爷’] , done:false }
// { value:['2',‘皇上’] , done:false }

函数泛型

// 函数泛型
正常写一个函数 进行类型规范
这种写法会比较麻烦
function fn(num:number):number{
  return num 
}
fn(123)

这样我们可以使用泛型进行约束
function fn<T>(str:T):T{
  return str
}
// 在调用函数时规定类型 那么 函数接受的参数就都是字符型
fn<string>('123')
// number就是数字类型
fn<number>(123)

// 也可以使用接口定义规范
// 定义一个接口
interface len {
  length:number
}

// 使用联合类型 将我们想到规定的类型 继承接口定义的类型
// 通过定义的 len 接口来约束 T 这个类型
function fn<T | len> (arg:T) {
  return arg.length
}
// 只能传一些带有 length 的值
fn([1,2,3,4])
fn('12346')

使用keyof约束对象

// 这个函数参数接受一个对象 还有一个对象的key
function prop<T>(obj:T,key){
  // 返回是这个key在对象中的值
  return obj[key]
}
// 对象可能是动态的
let o = { a:1,b:2,c:3 }

prop(o,'a') //在这里调用函数的返回值 就是 对象中 a 的值

//当我们传一个不存在的key时 ts 不会提示警告  这时 我们就需要用到 keyof 来拿到对象中的每一项
prop(o,'d') 

// 使用 keyof 来获取
// <T,K extends keyof T> 这里的 K 使用 keyof 继承 T 这个类型接受的值
// K 的值是一个联合类型 a | b | c 这样 在传一个不存在的值时 就会报错
function prop<T,K extends keyof T>(obj:T,key:K){
  // 返回是这个key在对象中的值
  return obj[key]
}
// 对象可能是动态的
let o = { a:1,b:2,c:3 }

prop(o,'a') //在这里调用函数的返回值 就是 对象中 a 的值

//当我们传一个不存在的key时 ts 不会提示警告  这时 我们就需要用到 keyof 来拿到对象中的每一项
prop(o,'d') 

泛型类

// 声明一个 class 类 使用泛型进行约束
class Sub<T>{
  attr:<T>[] = []
  add(a:T):<T[]>{
    return [a]
  }
} 

//  new Sub<number> 这个 number 代表了 约束类的 T 是number 类型 
//  那么在类中 所有使用 T 进行约束的值 都必须是number 类型
let s = new Sub<number>()
// 使用 T 约束的 attr 就是一个number类型的数组
s.attr = [1,2,3]	// 在使用时 就只能传数字类型的数组
s.add(123) // 这个函数也就是能接受 number 类型的值
  

可选链操作符 和 双问号表达式

// 可选链操作符 ?.
// 用法
let arr = {}
// 当我们声明了一个对象时 想读取里面的children属性
arr.children // 此时对象中是没有这个属性的 读取不到的时候会报undefined

// 当我们接着读取 children 的 length 长度
arr.children.length // 这时会报错 因为undefined属性是没有length属性的

// 这时我们可以用可选链操作符 ?.
arr.children?.length  // 这样 就不会报错了 而是返回undefined

// 双问号表达式 ??
// 在使用可选链操作符时 一般配饰 ?? 来使用
// 一般是判断左边的值 如果左边值为 null  或者 undefined 时 就会返回右边的值
// 也就是 左边为 null 或者 undefined 时就会返回右边的 {} 
// 注意 0 和 false 不做处理 不是反对左边为false时就会返回右边的值 
// 而是正常返回0 或 false
arr.children.length ?? {}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值