TypeScript

148 阅读6分钟

image.png

概念

image.png

  • 适用于大型项目,项目初期,会增加一些成本

快速上手

  • 安装: yarn add typescript
  • 编译命令: yarn tsc a.ts 编译后会生成一个js文件
const hello = (name:number) => {
    console.log('hello '+ name);
    
}
hello('cxl')  // 报错

配置文件

  • 命令: yarn tsc --init
  • .tsconfig.json
  • 整个项目编译命令: yarn tsc

原始类型

const a: string = 'foobar'
const b: number = 100 // NaN Infinity
const c: boolean = true // false
const e: void = undefined
const f: null = null
const g: undefined = undefined

// 在非严格模式(strictNullChecks)下,
string, number, boolean 都可以为空
const d: string = null
const d: number = null
const d: boolean = null

标准库

  • 内置对象所对应的声明文件

中文错误消息: tsc --locale zh-CN

作用域问题

(function () {
  const a = 123
})()
或者是:
// 解决办法2: 在当前文件使用 export,也就是把当前文件变成一个模块
// 模块有单独的作用域
const a = 123
export {}

Object类型

  • 不单指对象类型,这里包含对象、数组、函数类型
  • 单指对象
const obj: { foo: number, bar: string } = { foo: 123, bar: 'string' }

数组类型

// 数组类型的两种表示方式
const arr1: Array<number> = [1, 2, 3]
const arr2: number[] = [1, 2, 3]

// 如果是 JS,需要判断是不是每个成员都是数字
// 使用 TS,类型有保障,不用添加类型判断
function sum (...args: number[]) {
  return args.reduce((prev, current) => prev + current, 0)
}

sum(1, 2, 3) // => 6

元组类型

const tuple: [number, string] = [18, 'zce']  // 个数超出或者类型不对都会报错
const [age, name] = tuple

Object.entries({
  foo: 123,
  bar: 456
})
// 键值对就是元组 [string, number]

枚举类型

// 不取值,默认为0 1 2   如果Draft是6  则就是6 7 8 
const enum PostStatus {
  Draft,
  Unpublished,
  Published
}
// 加上const,在js中status展示: status: 0 /* Draft */ // 3 // 1 // 0

const post = {
  title: 'Hello TypeScript',
  content: 'Ty寒素peScript is a typed superset of JavaScript.',
  status: PostStatus.Draft // 3 // 1 // 0
}

函数类型

参数

  • 声明和调用的参数的个数必须一致
  • 可选参数和剩余参数必须放最后

可选参数: b?: number 或者是 b: number = 10

function func1 (a: number, b: number = 10, ...rest: number[]): string {
  return 'func1'
}

func1(100, 200)
func1(100)
func1(100, 200, 300)

函数表达式

const func2: (a: number, b: number) => string = function (a: number, b: number): string {
  return 'func2'
}

任意类型any

  • 不轻易使用any类型,兼容老代码可能会选择any类型
function stringify (value: any) {
  return JSON.stringify(value)
}

stringify('string')

隐式类型推断

let age = 18 // number
age = 'string' // 报错

let foo  // 推断为any类型
foo = 'string'  // 不会报错

类型断言

// 假定这个 nums 来自一个明确的接口
const nums = [110, 120, 119, 112]

const res = nums.find(i => i > 0)

// const square = res * res  // 直接用会报错,res可能是数字也可能是undefined

// 类型断言两种方式:
const num1 = res as number

const num2 = <number>res // JSX 下不能使用

interface接口

  • 一种规范,一种契约
  • 接口就是用来约束对象的结构,一个对象去实现一个接口,这个对象必须拥有这个接口当中所约束的所有成员
interface Post {
  title: string
  content: string
}

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

printPost({
  title: 'Hello TypeScript',
  content: 'A javascript superset'
})
  • 可选成员、只读成员、动态成员
interface Post {
  title: string
  content: string
  subtitle?: string  // 可选成员
  readonly summary: string  // 只读成员
}

const hello: Post = {
  title: 'Hello TypeScript',
  content: 'A javascript superset',
  summary: 'A javascript'
}
hello.summary = 'other'  // 报错,不可修改

interface Cache {
  [prop: string]: string // 动态成员
}

const cache: Cache = {}

cache.foo = 'value1'
cache.bar = 'value2'

基本使用

class Person {
  name: string // = 'init name'  // 先比js,ts必须先声明
  age: number  // 要嘛设置默认值,要嘛在构造函数中赋值,二选一
  
  constructor (name: string, age: number) {
    this.name = name
    this.age = age
  }

  sayHi (msg: string): void {
    console.log(`I am ${this.name}, ${msg}`)
  }
}

类的访问修饰符

class Person {
  public name: string // 默认是共有成员,最好加上public,增加可读性
  private age: number  // 私有成员,只允许内部访问
  protected readonly gender: boolean  // 受保护成员,允许继承
  // readonly只读属性,只能在初始化赋值或者是构造函数中赋值,二选一,在类的内部还是外部都不能被修改了
  
  constructor (name: string, age: number) {
    this.name = name
    this.age = age
    this.gender = true
  }

  sayHi (msg: string): void {
    console.log(`I am ${this.name}, ${msg}`)
    console.log(this.age)
  }
}

class Student extends Person {
  private constructor (name: string, age: number) { // private 私有构造函数,不允许new实例化,只能通过内部的静态方法create去实例化
    super(name, age)
    console.log(this.gender)
    console.log(this.age) // 报错 私有属性,只能在类Person中访问
  }

  static create (name: string, age: number) {
    return new Student(name, age)
  }
}

const tom = new Person('tom', 18)
console.log(tom.name)
console.log(tom.age) // 报错,私有成员外部不能访问
console.log(tom.gender) // 报错,受保护成员外部不能访问

const jack = Student.create('jack', 18)
const jack1 = new Student('jack', 18) // 类“Student”的构造函数是私有的,仅可在类声明中访问

类和接口

  • 类与类之间共同的特征可通过接口抽象化
  • 一般一个接口只约束一种能力
// 类与接口
interface Eat {
  eat (food: string): void
}

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

class Person implements Eat, Run {
  eat (food: string): void {
    console.log(`优雅的进餐: ${food}`)
  }

  run (distance: number) {
    console.log(`直立行走: ${distance}`)
  }
}

class Animal implements Eat, Run {
  eat (food: string): void {
    console.log(`呼噜呼噜的吃: ${food}`)
  }

  run (distance: number) {
    console.log(`爬行: ${distance}`)
  }
}

抽象类

  • 接口:不包含具体的实现,抽象类:包含具体的实现
  • 只能被继承,不能创建实例对象
// 抽线类

export {} // 确保跟其它示例没有成员冲突

abstract class Animal {
  eat (food: string): void {
    console.log(`呼噜呼噜的吃: ${food}`)
  }
  abstract run (distance: number): void // 抽象方法不需要方法体,子类必须要实现抽象方法
}

class Dog extends Animal {
  run(distance: number): void {
    console.log('四脚爬行', distance)
  }
}

const d = new Dog()
d.eat('嗯西马')
d.run(100)

泛型

  • 在定义函数、接口或类的时候不指定类型,在使用的时候再指定类型
  • 在定义时把不能明确的类型变成一个参数,在使用的时候再传递这个类型参数
function createNumberArray (length: number, value: number): number[] {
  const arr = Array<number>(length).fill(value)
  return arr
}

function createStringArray (length: number, value: string): string[] {
  const arr = Array<string>(length).fill(value)
  return arr
}

function createArray<T> (length: number, value: T): T[] {
  const arr = Array<T>(length).fill(value)
  return arr
}

// const res = createNumberArray(3, 100)
// res => [100, 100, 100]

const res = createArray<string>(3, 'foo')

类型声明

  • 一个成员在定义的时候由于种种原因没有一个明确的声明,在使用的时候单独对它做出明确的声明
  • 常用的npm模块都已经提供了对应的声明,只需要安装即可
  • 引用第三方模块,如果没有进行类型声明,就需要引入对应的类型声明,再没有的话,就要使用declare语句进行类型声明
import { camelCase } from 'lodash' // 需要安装 @types/lodash
import qs from 'query-string' // 不需要单独安装,模块中已包含对应的类型声明文件

qs.parse('?key=value&key2=value2')

// declare function camelCase (input: string): string // 没有类型声明文件需要通过declare进行声明

const res = camelCase('hello typed')