TypeScript

175 阅读5分钟

概述

  • JavaScript的超集
  • 解决JavaScript类型系统的问题
  • 前端领域的第二语言
  • 任何一种JavaScript运行环境都支持(浏览器、Node等)
  • 功能强大、生态健全
  • 属于渐进式

快速上手

yarn init --yes // 生成package.json管理依赖项

yarn add typescript --dev // node_modules/.bin 有tsc命令(编译ts代码)

yarn tsc test.ts // 生成同名的JS文件 test.js

yarn tsc --init // 生成tsconfig.json文件 直接运行yarn tsc 才走该配置文件

// tsconfig.json常用简单配置:
{
    "compilerOptions": {
       "target" : "es5",
       "module": "commonjs",       "lib": ["ES2015", "DOM", "ES2017"],        "sourceMap": true,     /* Generates corresponding '.map' file. */       "outDir": "dist",        "rootDir: "src",
       "strict": true              /* Enable all strict type-checking options. */
    }
}

原始数据类型

  • string
  • number
  • boolean
  • null
  • undefined
  • void
  • symbol
// 原始数据类型

const a: string = 'foobar'

const b: number = 100 // NaN Infinity

const c: boolean = true // false

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

const e: void = undefined

const f: null = null

const g: undefined = undefined

// Symbol 是 ES2015 标准中定义的成员,
// 使用它的前提是必须确保有对应的 ES2015 标准库引用
// 也就是 tsconfig.json 中的 lib 选项必须包含 ES2015
const h: symbol = Symbol()

// Promise

// const error: string = 100

数组类型

// 数组类型的两种表示方式

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 = tuple[0]
// const name = tuple[1]

const [age, name] = tuple

// ---------------------

const entries: [string, number][] = Object.entries({
  foo: 123,
  bar: 456
})

const [key, value] = entries[0]
// key => foo, value => 123

枚举类型

// 用对象模拟枚举
// const PostStatus = {
//   Draft: 0,
//   Unpublished: 1,
//   Published: 2
// }

// 标准的数字枚举
// enum PostStatus {
//   Draft = 0,
//   Unpublished = 1,
//   Published = 2
// }

// 数字枚举,枚举值自动基于前一个值自增
// enum PostStatus {
//   Draft = 6,
//   Unpublished, // => 7
//   Published // => 8
// }

// 字符串枚举
// enum PostStatus {
//   Draft = 'aaa',
//   Unpublished = 'bbb',
//   Published = 'ccc'
// }

// 常量枚举,不会侵入编译结果
const enum PostStatus {
  Draft,
  Unpublished,
  Published
}

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

// PostStatus[0] // => Draft

函数类型

对输入输出进行类型限制

// -------------------函数声明-------------------
// 参数可选表现为 ?符号或者添加默认值
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'
}

任意类型

不存在类型检查

function stringify (value: any) {
  return JSON.stringify(value)
}

stringify('string')

stringify(100)

stringify(true)

let foo: any = 'string'

foo = 100

foo.bar()

// any 类型是不安全的

隐式类型推断

let age = 18 // number

// age = 'string'

// 如果typescript无法推断一个变量的类型,则将这个变量的类型标记为any
let foo

foo = 100

foo = 'string'

// 建议为每个变量添加明确的类型标注

类型断言

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

const res = nums.find(i => i > 0) // typescript推断res类型为number或undefined

// const square = res * res

const num1 = res as number

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

接口

约定对象中有哪些成员以及成员的类型,约束对象的结构

// -----------------接口基础-----------------
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'

用来描述一类具体对象的抽象成员,Typescript增强了class的相关语法。

基本使用

class Person {
  name: string // = 'init name'
  age: number
  
  constructor (name: string, age: number) {
    this.name = name
    this.age = age
  }

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

类的访问修饰符

  • private (类内访问)
  • public(默认)
  • protected(类内、子类访问)
class Person {
  public name: string // = 'init name'
  private age: number
  protected gender: boolean
  
  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 {
  // constructor默认是public,若设为private,则不可在外实例化,也不可被继承
  private constructor (name: string, age: number) {
    super(name, age)
    console.log(this.gender)
  }
 // 可通过静态方法创建实例
  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)

类的只读属性

在声明或构造时初始化

class Person {
  public name: string // = 'init name'
  private age: number
  // 只读成员
  protected readonly gender: boolean
  
  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)
  }
}

const tom = new Person('tom', 18)
console.log(tom.name)
// tom.gender = false

类与接口

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}`)
  }
}

抽象类

只能被继承,不能被实例化,可以包含一些实现

子类必须实现父类的抽象方法

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')

类型声明

如果第三方模块不存在类型声明,可用 yarn add @types/模块名 安装,也可手动 declare

import { camelCase } from 'lodash'

// declare function camelCase (input: string): string

const res = camelCase('hello typed')

进阶使用

juejin.cn/post/684490…