TypeScript

174 阅读11分钟

typeScript基础使用

  1. 安装yarn add typescript --dev
  2. 运行yarn tsc ts文件 进行编译转换成js代码

typeScript配置文件

  1. 使用命令生成配置文件 yarn tsc --init
  2. 单独编译tsc文件,tsconfig.json不会生效
  3. 运行yarn tsc

6种原始数据类型在typescript中的基本使用

/**
 * 原始数据类型
 */
const a: string = '123'
const b: number = 123//NaN Infinity包括其中
const c: boolean = true//只能存放true 或者false
const d: string = undefined //string、number、boolean可以设置为空值,可以设置null或者undefined,但是在严格模式下是不能进行设置的,默认情况下可以即在tsconfig.json中的"strict": false

const e: void = undefined//函数返回值为空值或者没有函数返回值时,默认情况下只能设置为null或者undefined,但是在严格模式的情况下只能是undefined
const f: null = null
const g: undefined = undefined
const h:symbol=Symbol()//此种类型只能存放symbol的值,在无配置的情况下会报错需要在tsconfig.json中的`lib` 编译器选项更改为 es2015 或更高版本

typescript标准库声明(内置对象所对应的声明)

  1. 更改标准库方法1
"target": "es5",==>"target": "es2015",
  1. 更改标准库方法2
"lib": ["es2015","DOM"],//注意设置时需要带上bom和dom的默认设置"DOM"

中文错误消息(让typescript报错显示中文提示)

  1. 针对命令行中错误消息显示中文,直接通过打包命令yarn tsc --locale zh-CN
  2. 针对代码中提示错误信息显示中文-在文件/首选项/设置中搜索typescript locale,将第一个中选项中null更换成zh-CN setting.png

作用域问题(重复定义变量,导致页面报错)

  1. 立即执行函数包裹
(function(){
  const a=123
})()
  1. 通export导出的方式
const a=123
export {}

Object类型(对象自变量声明的结构,最好是用接口的方式声明)

// Object类型
export {} //确保跟其他实例中定义的成员冲突
const foo:object=function(){}//可以接受对象{}/数组[]/函数function(){}
const obj:{foo:number,bar:string}={foo:1,bar:'123'}//声明普通对象需要对象自变量方式,赋值的对象类型结构里面必须与后面的对象结构完全一致

数组类型

// 数组类型

// 声明方式方法一:
const arr1: Array<number> = [1, 2, 3]
// 声明方式方法二:
const arr2: number[] = [1, 2, 3]

// 判断传入sum函数中参数必须是数字类型
function sum(...args: number[]) {
  return args.reduce((prev, current) => prev + current, 0)
}

sum(1,2,3)

元组类型

// 元组(tuple)
export { }//确保跟其他成员没有成员冲突
const tuple: [number, string] = [123, '123']//元组中的数据类型有几种,数组只能存在相对应的数据
//元组数据可以通过下标的方式进行访问
const age = tuple[0]
const name = tuple[1]
// 通数据结构的方式提取数据
const [age1, name1] = tuple

枚举类型

枚举有两个特点:

  1. 可以给一组数值分别取一个很好理解的名字
  2. 一个枚举中只能存在几个固定的值,不会存在超出范围
// 枚举(enum)
export { }//确保跟其他示例没有成员冲突

// 枚举,枚举的使用和对象的使用一样,直接通过PostStatus.Draft方式使用
enum PostStatus {
  // 具体的枚举值
  Draft = 0,
  Unpublished = 1,
  Public = 2
}

// 枚举特殊(数值会自增长-累加):枚举中的值可以不用等号指定,则默认枚举中的值会从零开始进行累加,如果指定枚举中的第一个值,后面的值没有指定,则会从第一个值进行累加
enum PostStatus1 {
  // 具体的枚举值
  Draft,//Draft=6
  Unpublished,
  Public
}


const post = {
  title: 'hello TypeScript',
  content: 'TypeScript is a typed superset of Javascript',
  status: PostStatus.Draft
}

枚举类型会入侵运行时的代码(影响编译后的结果),会编译成双向键值对象(可以键获取值,也可以通过值获取键)

enum PostStatus1 {
  // 具体的枚举值
  Draft,//Draft=6
  Unpublished,
  Public
}
// 可以使用索引的方式获取枚举的名称
PostStatus1[0]//Draft

IMG_1985.PNG 如果确定代码中不会使用索引的方式获取枚举名称,就使用常量枚举

// 如果确定代码中不会使用索引的方式获取枚举名称,就使用常量枚举 
const enum PostStatus2 {
  // 具体的枚举值
  Draft,//Draft=6
  Unpublished,
  Public
}

IMG_1986.PNG

函数类型

  1. 函数声明
  2. 函数表达式
// 函数类型
export { } //确保跟其他示例没有成员冲突

// 函数声明
function func1(a: number, b: number): string {
  return 'func1'
}
func1(100, 200)//必须确保形参和实参的个数保持一致

// 函数声明中的可选参数和参数默认值(注意可选参数后参数默认值必须在所有的参数最后面)
//第一种即在参数后加个问号
function func2(a: number, b?: number): string {
  return 'func1'
}
func2(100)//在调用时可传可以不传
//第二种直接在参数后加默认值
function func3(a: number, b: number = 10): string {
  return 'func1'
}
func3(100)

// 任意参数
function func4(a: number, b: number = 10, ...rest: number[]): string {
  return 'func1'
}
func4(100)


// 函数表达式(fun5是接收值,所以也需要进行值的声明)
const fun5: (a: number, b: number) => string = function (a: number, b: number): string {
  return 'fun5'
}

任意类型(any)typescript不会对any类型进行类型检查,所以导致any类型不安全

// 任意类型(弱类型)
export {} //确保跟其他示例中没有成员冲突

function stringify(value:any){
  return JSON.stringify(value)
}
stringify('string')
stringify(100)
stringify(true)
// 声明any类型后可以值进行任意赋值操作
let foo:any='string'
foo=100
foo.bar()

隐式类型推断(在声明时,直接进行赋值操作,就会推断成相应的类型)

//隐式类型推断
export { }//确保跟其他示例没有成员冲突

// 在声明时,直接进行赋值操作,就会推断成相应的类型
let age = 18//此处会将age变量隐式推断为number类型,在后面进行再次赋值时,只能赋值为数字类型

let string//此处会将string变量隐式推断为any类型,在后面进行再次赋值时,可以赋值为任意类型
string = 123
string = '123'
string = true

类型断言(代码在运行时,typescript无法确定变量的值是什么类型,但开发者知道最后的变量的值是什么类型,此时就需要进行断言)

  1. 通过as关键词断言
  2. 在变量前面使用<>(尖括号)方式断言
// 类型断言
const nums = [110, 120, 119, 112]
const res = nums.find(i => i > 0)//typescript无法确定变量的值是什么类型,const res:number|undefined(typescript会提示两种类型)
// 第一种方式断言——as(推荐)
const nums1 = res as number
// 第二种方式断言——<>
const num2 = <number>res//在react的jsx中不能使用,会导致语法冲突

接口(为了约束一个对象的结构,一个对象要实现一个接口,则必须拥有接口中所有成员)

  1. 接口的基本使用
export { }

interface Post {//实现接口类型的关键字interface
  title: string
  content: string
}

function printPost(post: Post) {
  console.log(post.title)
  console.log(post.content)
}
  1. 接口可选成员、只读成员、动态成员
export { }

interface Post {
  title: string
  content: string
  subtitle?: string//可选成员
  readonly summary: string//只读成员,声明后值就不能进行更改
}

function printPost(post: Post) {
  console.log(post.title)
  console.log(post.content)
}

// 动态成员
interface Cache {
  // prop可以用任何的值进行代替 [key:string]:string
  [prop: string]: string//[]中指定动态成员的键的类型,外面指的是值的类型
}
const cache: Cache = {}//声明后可以在cache中动态添加成员,但是成员的值类型必须是字符串
cache.foo = 'value'

类class

  1. 类的基本使用
// 类 class
export { }
class Person {
  // 类的属性必须有个初始值
  name: string//可以直接赋值 name:string='123'
  age: number
  constructor(name: string, age: number) {//在typescript中必须明确声明类的中所用的属性,不能动态通过this进行添加
    this.name = name
    this.age = age
  }
  sayhi(msg: string): void {
    console.log(`I am ${this.name},${msg}`)
  }
}
  1. 类的访问修饰符
  2. 在属性前加访问修饰符
  • public关键字会默认加在属性之前,表示公共的
  • private关键词表示属性是类的私有属性,只能在类内部访问,无法在外部直接访问
  • protected关键词表示受保护的,只允许在子类中进行访问,无法在外部直接访问
  1. 构造函数(constructor)访问修饰符
  • private关键词表示是类的私有的构造函数,只能在类的内部使用构造函数即实例化(通过static静态属性抛出),无法在外面进行实例化
  • protected关键词只能在字类的中进行构造函数,无法在外面直接使用
// 类的访问修饰符
class Person1 {
  // public关键字会默认加在属性之前,表示公共的
  public name: string
  private age: number//在age前加private关键词,则age是Person1的私有属性,只能在类的内部访问
  protected gender: boolean //protected关键词,表示受保护的,在外部也无法访问,只允许在子类中进行访问
  constructor(name: string, age: number) {//在typescript中必须明确声明类的中所用的属性,不能动态通过this进行添加
    this.name = name
    this.age = age
    this.gender = true
  }
  sayhi(msg: string): void {
    console.log(`I am ${this.name},${msg}`)
  }
}
const tom = new Person1('123', 123)
//console.log(tom.age)此时age无法在外部进行访问
//console.log(tom.gender)此时age无法在外部进行访问

class Student extends Person1 {
  constructor(name: string, age: number) {
    super(name, age)
    console.log(this.gender)//可以在子类中访问到父类中gender的属性
  }
}

// 构造函数(constructor)的访问修饰符private
class Student1 extends Person1 {
  // private关键字会将Student1构造函数无法在外面进行实例化
  private constructor(name: string, age: number) {
    super(name, age)
    console.log(this.gender)//可以在子类中访问到父类中gender的属性
  }
  // 只能通过static在类的内部进行构造函数,在再外面使用
  static create(name: string, age: number) {
    return new Student1(name, age)
  }
}
const tom1 = Student1.create('name', 12)

// 构造函数的访问修饰符protected,只能在字类的中进行构造函数,无法在外面直接使用
  1. 类的只读属性 readonly 如果类的属性之前有访问修饰符则直接在修饰符后面加上readonly(protected readonly gender: boolean)

类与接口(将不同的类与类之间的共同特征,通过接口进行抽象)

// 类与接口
export { }
// 通过接口对公共的部分进行约束

interface EatAndRun {
  eat(food: string): void
  run(distance: number): void
}

class Person implements EatAndRun {
  eat(food: string): void {
    console.log(`优雅的进餐${food}`)
  }
  run(distance: number) {
    console.log(`站立行走:${distance}`)
  }
}
class Animal implements EatAndRun {
  eat(food: string): void {
    console.log(`呼噜呼噜${food}`)
  }
  run(distance: number) {
    console.log(`爬:${distance}`)
  }
}


// 让一个接口约束多个公共部分,就是将以前写在一起的接口进行拆分
interface eat {
  eat(food: string): void
}

interface run {
  run(distance: number): void
}

class Person1 implements eat,run {
  eat(food: string): void {
    console.log(`优雅的进餐${food}`)
  }
  run(distance: number) {
    console.log(`站立行走:${distance}`)
  }
}
class Animal1 implements eat,run {
  eat(food: string): void {
    console.log(`呼噜呼噜${food}`)
  }
  run(distance: number) {
    console.log(`爬:${distance}`)
  }
}

抽象类(abstract)

  1. 如果类被定义成抽象类后,只能被继承,不能通过new的方式创建实例对象了,必须通过子类继承
  2. 抽象类中也可以定义抽象方法(在方法前加abstract),抽象方法不需要方法体,如果父类中存在抽象方法,则在子类中必须存在对应的方法实现
// 抽象类
abstract class name {
  eat(food: string): void {
    console.log(food)
  }
  // 抽象方法
  abstract food(food1: string): void
}

class name1 extends name {
  // 父类的抽象方法必须在子类中实现
  food(food1: string): void {
    console.log('1111')
  }
}
const Name = new name1()
Name.eat('dx')

泛型(generics)

在定义函数、接口、类的时候没有指定具体的类型,在使用时才进行指定(主要是为了复用代码)

// 泛型
export { }
// 创建一个生成数字类型的数组
function NumberArr(length: number, value: number): number[] {
  // Array对象创建的使用any类的数组,所以需要Array创建的数组类型,通过泛型参数指定类型<>
  const arr = Array<number>(length).fill(value)
  return arr
}

// 创建一个生成字符串类型的数组
function StringArr(length: number, value: string): string[] {
  // Array对象创建的使用any类的数组,所以需要Array创建的数组类型,通过泛型参数指定类型<>
  const arr = Array<string>(length).fill(value)
  return arr
}


// 由于上面的函数只能生成当个类型的数组,所以可以通过泛型,将类型转换成参数进行传递,泛型的参数用大写的T,将函数内容中不明确的类型用T进行代表
function creatArr<T>(length: number, value: T): T[] {
  const arr = Array<T>(length).fill(value)
  return arr
}
const res = creatArr<number>(3, 3)

类型声明(type declaration)

由于第三方的npm模块,而npm的模块不一定是用typeScript进行编写,导致提供的成员不会有强类型的体验(例如:lodash)

// 类型声明

export { }

// 导入lodash
import { camelCase } from 'lodash'

//如果安装的第三方插件没有类型声明的文件,可以通declare进行类型指定
// declare function camelCase(input: string): string

const res = camelCase('hello')