TypeScript 语言

231 阅读5分钟

TypeScript 解决了 JavaScript 类型系统的问题,大大提高代码的可靠程度

强类型 vs. 弱类型 (类型安全)

强类型

  • 强类型语言层面限制函数的实参类型必须与形参类型相同
  • 强类型语言中不允许任意的隐式类型转换
  • 错误更加早暴露
  • 代码更智能,编码更准确
  • 重构更牢靠
  • 减少不必要的类型判断

弱类型

  • 弱类型语言层面不会限制实参的类型
  • 弱类型语言则允许任意的数据隐式类型转换

非权威说法,没有权威说法

静态类型 vs. 动态类型 (类型检查)

静态类型语言

  • 一个变量声明的时候,它的类型就是明确的,之后不允许在修改

动态类型语言

  • 在运行阶段才能够明确变量的类型,之后也可以随时改变
  • 换言之,动态类型语言的变量是没有类型的,变量中存放的值是有类型的

JavaScript 类型系统特征

  • 弱类型
  • 动态类型
  • 缺失了类型系统的可靠性
  • JavaScript 没有编译环节

JavaScript 弱类型产生的常见问题

const obj = {}

obj.foo() // 运行时才发现错误,而非类型检查阶段

setTimeout(() => {
  obj.foo()  // 运行时才发现错误,而非类型检查阶段
}, 100000)

function sum (a, b) {
  return a + b
}

console.log(sum(100, 100))
console.log(sum('100', 100)) // 发生隐式类型转化,但不会报错提示

const obj = {}
obj[true] = 100
console.log(obj['true']) // 虽然可以获取,前后形式不一样,让人感到奇怪

Flow

FlowJavaScript 的类型检查器

Flow 原始类型

/**
*@flow
*/
const a: string = 'foobar
const b: number = Infinity // NaN // 100
const c: boolean = false // true
const d: null = null
const e: void = undefined
const f: symbol = Symbol()

Flow 数组类型

/**
*@flow
*/

const arr1: Array<number> = [1, 2, 3]
const arr2: number[] = [1, 3, 4]

// 元组
const foo: [string, number] = ['foo', 100]

Flow 对象类型

/**
*@flow
*/
const obj1: {
    foo: string,
    bar: number
} = {
    foo: 'string',
    bar: 100,
}

const obj2: {
    foo?: string,
    bar: number,
} = {
    bar: 100
}

const obj3: { [string]: string } = []

obj3.key1 = 'dsad'
obj3.key2 = 'dsadsad'

Flow 函数类型

/**
*@flow
*/
function foo (callback: (string, number) => void) {
    callback('string', 100)
}

foo(function (str, n) {
    // str => string
    // n => number
})

Flow 特殊类型

/**
*@flow
*/
const a: 'foo' = 'foo'
const type: 'success' | 'warning' | 'danger' = 'success'

type StringOrNumber = 'string' // 100

const b: StringOrNumber = 'string' // 100

const gender: ?number = undefined

const gender: number | null | void = undefined

Flow Mixed 与 Any

/**
*@flow
*/
function passMixed (value: mixed) {
    if (typeof value === 'string') {
        value.substr(1)
    }
}


function passAny (value: any) {
    value.substr(1)
}

TypeScript

TypeScript 是 JavaScript 的超集

  • JavaScript + ES6+ + 类型系统 = TypeScript (最终编译成) JavaScript

  • 任何一种 JavaScript 运行环境都支持

  • 相比 Flow, TypeScript 功能更为强大, 生态更健全、更完善

  • TypeScript 已成为 前端领域中的第二语言

TypeScript 原始类型

const a: string = 'foobar'

const b: number = 100

const c: boolean = true

const e: void = undefined

const f: null = null
const g: undefined = undefined

const h: symbol = Symbol()

TypeScript 作用域问题

// ts1.ts
/**
(function () { 
    console.log(a)
})()
**/

const a {}
export {}

// ts2.ts
const a = 234
export {}

TypeScript Object 类型

原始类型以外的其他类型,不仅仅是对象

const foo: Object = function () {} // [] // {}

const obj: { foo: number, bar: string } = { foo: 123, bar: 'string' }

TypeScript数组类型

const arr1: Array<number> = [1, 2, 3]
const arr2: number[] = [4, 5, 6]

// ___________________________________________________

function sum (...args: number[]) {
  return args.reduce((pre, current) => pre + current, 0)
}
sum(1, '2', 3) // Argument of type 'string' is not assignable to parameter of type 'number'.ts(2345)
sum(2, 3, 4)

TypeScript 元组类型

const tuple: [number, string] = [18, 'saj']

const age = tuple[0]
const name = tuple[1]

const [age, name] = tuple

TypeScript 枚举类型

枚举会影响编译后的结果,最终编译成双向的键值对对象

enum PostStatus {
    Draft = 1
    dsadad, // 2
    vdxv,  // 3
}
/** 其他逻辑 **/

// 编译后生成的js
var PostStatus;
(function (PostStatus) {
    PostStatus[PostStatus["Draft"] = 1] = "Draft";
    PostStatus[PostStatus["dsadad"] = 2] = "dsadad";
    PostStatus[PostStatus["vdxv"] = 3] = "vdxv"; // 3
    
    /** 其他逻辑 **/
    
})(PostStatus || (PostStatus = {}));

// ---------------------------------------------
// 常量枚举就不会生成双向的键值对对象
const enum PostStatus {
    Draft = 1
    dsadad, // 2
    vdxv,  // 3
}

// 编译后的js
var post = {
    status: 1 /* Draft */
};

TypeScript 函数类型

对函数的输入输出类型约束

function func1 (a: number, b: number, ...rest: number[]): string {
  return (a + b).toString()
}

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

TypeScript 任意类型

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

TypeScript 隐式类型推断

let age = 18 => let age: number =  18
age = 'string' // Type 'string' is not assignable to type 'number'.ts(2322)

TypeScript 类型断言

const nums = [110, 120, 119, 112]

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

const num1 = res as number
const num2 = <number>res // jsx 不能使用

TypeScript 接口 (类型约束,没有生成具体代码)

interface Post {
    title: string
    content?: string // 可选成员
    readonly summary: string // 只读成员
    [key: string]: string // 动态属性
}
function printPost (post: Post) {
    console.log(post.title)
}

printPost({
    title: 'Hello'
})

TypeScript 类的基本使用

增强了 class 的相关语法

class Person {
  public name: string // 默认公有属性
  private age: number // 私有属性 // 只允许在当前类访问,实例和子类不能访问
  protected readonly gender: boolean // 受保护属性,只允许在子类中访问

  private constructor (name: string, age: number) {
    this.name = name // 必须初始化
    this.age = age // 必须初始化
    this.gender = true
  }

  sayHi (msg: string): void {

    console.log(this.name, msg)
  }
  
  static create (name: string, age: number) {
     return new Person(name, age)
  }
}

TypeScript 类和接口

接口功能尽可能单一,让类去继承多个接口


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

TypeScript 抽象类

抽象类可以包含具体实现, 只能被继承,不能创建实例

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

TypeScript 泛型

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
}

TypeScript 类型声明

很多第三方模块都有类型声明文件,如果没有,就用declare创建一个类型类型声明模块

import { camelCase } from 'lodash'

declare function camelCase(params: string): string

const res = camelCase('hello typed')