一份虽迟但到的TypeScript学习笔记

206 阅读9分钟

虽迟但到,都2022了,总该花时间看看ts吧

因为js是动态弱类型语言,为给js提供静态类型检查,提出了typeScript/flow

  • 弱类型:例如var a=1,a=“String”,给a定义可以赋不同类型的值
  • 静态类型:在编译阶段确定所有变量的类型
  • 动态类型:在执行阶段确定变量类型

ECMAScript标准 推出静态类型检查之前,typeScript是解决次问题的最佳方案

什么是typeScript?

TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准

三个要点

  1. 类型检查:在编译时进行类型检查,在编码阶段发现可能存在的隐患
  2. 语言拓展:typeScript会包括来自es6和未来提案中的特性,比如异步操作和装饰器,也从其他语言借鉴特性,如接口和抽象类
  3. 工具属性:typeScript可以编译成标准的JavaScript,typeScript更像是工具

typescript的数据类型

image.png

typescript的常用类型

原始类型:number/string/boolean/null/undefined/symbol 对象类型:object(对象、数组、函数等) 联合类型、自定义类型(类型别名)、接口、元组、字面量类型、枚举、void、any

1.类型注解

作用:为变量添加类型约束,相当于强类型语言中的类型声明。 解释:约定了什么类型,就只能给变量赋值该类型的值,否则就报错。 语法:(变量/函数):type

2.常用基础类型

let age: number = 18
let name: string = '名称'
let isloading: boolean = false
let a: null = null
let b: undefined = undefined
let b: symbol = symbol()

2.数组类型

let numbers: number[] = [1,2,3]
let strings: Array<string> = ['a','b','c']

// 联合类型 数组中既有number类型又有string类型
let arr:(number | string)[] = [1,'a','b',2]

3.类型别名(type)

类型别名:为任意类型起别名 使用场景:当同一个类型多次使用,可以通过类型别名,简化该类型使用。

type CustomArray = (number | string)[]
let arr1:CustomArray = [1,'a','b',2]
let arr2:CustomArray = ['a',1,'b',2]

4.函数类型

1.单独指定参数、返回值类型
function add(num1:number, num2:number):number{
  return num1 + num2
}
const add = (num1:number, num2:number):number=>{
  return num1 + num2
}

2.同时指定参数、返回值类型
const add:(num1:number, num2:number)=>number = (num1,num2)=>{
  return num1 + num2
}

3.函数没有返回值。viod
function myName(name: string):void{
  console.log('hello',name)
}

4.可选参数: 在可传可不传的参数名称后面添加?
function myName(a?:string,b?:string):void{
  console.log('第一个参数',a,'第二个参数',b)
}

5.对象类型

1.使用{}来描述,属性名:类型; 方法名():返回值类型
let person:{name:string; age:number; sayHi():void}={
  name:'xiaoming',
  age:18,
  sayHi(){}
}

2.可选属性/方法,可传可不传属性后加?
function myGet(config:{ url: string; method?:string }){
  console.log(config)
}

6.接口(interface)

1.使用关键字interface声明
interface IPerson{
  name: string
  age: number
  sayHi():void
}

let person1: IPerson{
  name:'xiaoming',
  age:18,
  sayHi(){}
}

let person2: IPerson{
  name:'xiaohong',
  age:16,
  sayHi(){}
}


2.⚠️注意:类型别名和接口的对比
相同:都可以给对象指定类型
不同:接口,只能为对象指定类型,类型别名可以为任意类型指定别名


3.接口继承:使用extends关键字
interface IPerson{
  name: string
  age: number
  height: number
  sayHi():void
}

interface MyPerson extends IPerson{
  weight: number
}

7.元组(Tuple)

元组类型是另一种类型的数组,它确切包含多少个元素,和特定索引对应的类型。

let position:[number,string] = [1,'xiaoming']

8.类型推论

Typescript的类型推论机制会帮助提供类型,即类型注视可以不写 1.声明变量并初始化时 2.决定函数返回值时

9.类型断言

什么情况下需要用类型断言,当更加明确知道元素类型的时候,使用类型断言 使用 as 关键字 或者使用<>语法

const alink = document.getElementById('link') as HTMLAnchorELement

// 或者
const alink = <HTMLAnchorELement>document.getElementById('link') 

补充一个小技巧,如何获取标签的更加具体的类型,通过浏览器的控制台console.dir(0)这个0) 这个0 是在控制台html标签那里查看。

10.字面量类型

可以用字符串、数字、对象作为类型,
使用场景:一般配合联合类型使用,用来表示一组明确的可选值列表

例子: function changeDirection(direction:'up'|'down'|'left'|'right'){}
表示参数direction的值只能是up/down/left/right中任意一个

11.枚举 (enmu)

enmu Direction{
  up,
  down,
  left,
  rihgt
}

数字枚举:枚举的成员都有值,默认是0开始递增,也可以设置值
enmu Direction{
  up = 10,
  down,
  left,
  rihgt
}

字符串枚举:也可以设置为字符串
enmu Direction{
  up = 'UP',
  down = 'DOWN',
  left = 'LEFT ',
  rihgt = 'RIGHT'
}

function changeDirection(Direction){}

// 通过 .访问枚举的值
changeDirection(Direction.up)

值得一知:其他类型在编译为js代码时会自动移除,但枚举类型会在编译为js代码

12.any类型 (不推荐使用,会失去ts的意义)

当值的类型为any时候,可以对该值进行任意操作,不会有代码提示
let obj: any = {}

13.typeof

ts中的typeof可以在类型上下文中引用变量或属性的类型(类型查询) 使用场景:根据已有变量的值,获取该值的类型,来简化书写

typescript的高级类型

calss类

// class 基本使用
class Person {
  age: number,
  gender = '男' //可以省略类型注解,TS类型推论为string类型
}

// class 构造函数
class Person {
  age: number,
  gender = '男'
  
  constructor(age: number, gender: string){
    this.age = age
    this.gender = gender
  }
}

const p = new Person(16, '女')
console.log(p.age, p.gender) // 16 女


// class 实例方法
class Point {
  x = 1
  y = 2
  
  scale(n: number){
    this.x *= n
    this.y *= n
  }
}

const p = new Point()
p.scale(10)
console.log(p.x, p.y) // 10 20

// class 类继承的两种方式 1、extends(继承父类) 2、implements(实现接口)
// 1、extends 继承父类
class Animal{
  move(){
    console.log("Animal的move方法")
  }
}
class Dog extends Animal{
  name = '小狗'
  bark(){
    console.log("Dog的bark方法")
  }
}
const d = new Dog()
d.move() // Animal的move方法
d.bark() // Dog的bark方法
console.log(d.name) // 小狗

// 2、implements 实现接口
interface Singable {
  sing(): void
  count: number
}

class Person implements Singable{
  // Person类中必须提供Singable接口中指定的所有方法和属性
  count: 5
  sing(){
    console.log('唱歌')
  }
}

// class的可见性修饰符1、public(公有的)2、protected(受保护)3、private()
class Animal{
//  public move(){
//  protected move(){ //仅对声明所在的类和子类中(非实例对象)可见
  private move(){ //只在当前类中可见,实例对象以及子类都不可见
    console.log("Animal的move方法")
  }
}
class Dog extends Animal{
  name = '小狗'
  bark(){
    console.log("Dog的bark方法")
  }
}

// class的 只读修饰符readonly(只能修饰属性,不能修饰方法)
class Animal{
  // 表示只读,构造函数之外无法对属性进行赋值
  readonly age: number = 2
  constructor(age: number){
    this.age = age
  }
}

类型兼容性

// 类兼容性
class Point{x: number, y: number}
class Point2D{x: number, y: number}
class Point3D{x: number, y: number, z: number}

const p: Point = new Point2D()
const p: Point = new Point3D() //成员多的可以赋值给成员少的

// 接口兼容性
interface Point{x: number, y: number}
interface Point2D{x: number, y: number}
let p1: Point
let p2: Point2D = p1

interface Point3D{x:number, y:number, z:number}
let p3: Point3D
p2 = p3 

// 函数之间兼容性,需要考虑参数个数、参数类型、返回值类型
参数少的可以赋值给参数多的
相同位置的参数类型要相同或者兼容
只关注返回值类型本身即可

交叉类型 &

交叉类型 (&):用于组合多个类型为一个类型(常用于对象类型)

// 交叉类型使用
interface Person{
  name: string,
  say(): number
}
interface Contact{
  phone: string
}

type PersonDetail = Person & Contact
let obj:PersonDetail = {
  name:'xiaoming',
  phone:'123'
  say(){
    return 1
  }
}

// 交叉类型(&)和接口继承(extends)对比
相同:都可以实现对象类型的组合
不同点:两种方式实现类型组合时,对于同名属性,处理类型冲突方式不同

泛型

泛型是在保证类型安全的同时,让函数等与多种不同类型一起工作,从而实现复用,常用于:函数、接口、class中。

// 一、泛型函数
function 函数名<指定具体类型>(value: 指定具体类型): 指定具体类型{return 值}
如:
function id<Type>(value: Type): Type{ 
  return value
}
调用
const num = id<number>(10)
const str = id<string>('1')
const ret = id<boolean>(false)

简化调用泛型函数:因为TS内部会通过类型参数推断的机制,自动推出类型
let num = id(10)
let str = id('1')
let ret = id(false)

// 泛型约束:泛型函数的类型变量Type可以表示多个类型,但是无法访问任何属性
// 添加泛型约束收缩类型,1、指定更加具体的类型,2、添加约束
function id<Type>(value: Type): Type{
  console.log(value.length) //无法访问length
  return value
}
// 1.指定更加具体的类型
function id<Type>(value: Type[]): Type[]{
  console.log(value.length) // 修改了Type为Type[],就可以访问数组的属性方法
  return value
}
// 2.添加约束
interface ILength { length: number}
function id<Type extends ILength>(value: Type): Type{
  console.log(value.length) // <Type extends ILength> 这样写达到访问length的目的
  return value
}
id(['1','2'])
id('abcd') //字符串有length属性
id({length:5, name:'xiaoming'}) //传有对象可以,对象要有length属性,可以多传属性
id(100) // 这个错误,因为100是数字类型,没有length属性

// 多个泛型变量的情况( keyof )
// 使用keyof关键字 接收一个对象类型,生成其键名的联合类型
function getProp<Type, Key extends keyof Type>(obj: Type, key:key){
  return obj[key]
}
getProp({name:'xiaoming', age: 18}, 'name') //实现了访问对象中存在的属性


// 二、泛型接口
interface idFunc<Type>{
  id: (value: Type) => Type
  ids: () => Type
}
let obj: idFunc<number> = {
  id(value) { return value }
  ids() { return [1,2,3]}
}

// 三、泛型类
class GenericNumber<NumType>{
  defaultValue: NumType
  add: (x: NumType, y: NumType)=> NumType
}
const myNum = new GenericNumber<number>()
myNum.defaultValue = 10

// 四、泛型工具类型 
Partial<Type> 用来构造一个类型,将Type的所有属性设置为可选
Readonly<Type> 用来构造一个类型,将Type的所有属性都设置为readonly(只读)
Pick<Type,Keys> 从Type中选择一组属性来构造新类型
Record<Keys,Type> 构造一个类型,属性键为Keys,属性类型为Type


索引签名类型

索引签名类型使用场景:当无法确定对象中有哪些属性时,可以使用索引签名类型

//索引签名类型
interface AnyObject {
  [key: string]: number
}
let obj: AnyObject = {
  a: 1,
  b: 2
}

映射类型

映射类型:基于旧类型创建新类型,减少重复。(只能在类型别名中使用,不能在接口中使用)

type Keys = 'x' | 'y' | 'z' | 'a' 
type Type1 = {x: number, y: number, z: number, a: number} // 普通的方式,需要每一个都写
type Type2 = {[key in Keys]: number} // 通过[key in 遍历的类型],实现

//映射类型 keyof,根据一个对象类型生成一个对象类型
type Props = {a: number, b: string, c: number}
type Type3 = {[key in keyof Props]: number}