内容概要

  • 强类型与弱类型
  • 静态类型与动态类型
  • JavaScript 自有类型系统的问题, 弱类型、动态类型语言
  • Flow静态类型检查方案
  • TypeScript语言规范与基本应用


类型系统

  • 强类型与弱类型(类型安全)
    • 强类型:语言层面限制函数的实参类型必须与形参类型相同
    • 弱类型:弱类型语言层面不会限制实参的类型
    • 区别
      • 强类型有更强的类型约束,而弱类型中几乎没有什么约束
      • 强类型语言中不允许任意的隐式类型转换,而弱类型语言则允许任意的数据隐式类型转换
    • 强类型优势
      • 错误更早暴露
      • 代码更智能,编码更准确
      • 重构更牢靠
      • 减少不必要的类型判断
  • 静态类型与动态类型(类型检查)
    • 静态类型:一个变量声明时他的类型就是明确的,声明过后,他的类型就不允许再修改
    • 动态类型:运行阶段才能够明确变量类型,而且变量的类型随时可以改变
      • 动态类型的语言中,变量是没有类型的,变量中存放的值是有类型的

js 没有编译环节, 所以设计成强类型语言也没有什么意义
缺点:大规模应用下,这种「优势」就能变成了短板

TypeScript

JavaScript的超集(superset)

  • 任何一种javaScript运行环境都支持
  • 功能更为强大,生态也更为健全、更完善
  • TypeScript —- 前端领域中的第二语言
  • 缺点
    • 语言本身多了很多概念
    • TypeScript属于渐进式
    • 项目初期,TypeScript会增加一些成本

数据类型

原始数据类型

  1. const a: string = "foobar"
  2. const b: number = 100
  3. const c: boolean = true // false
  4. const e: void = undefined
  5. const f: null = null
  6. const g: undefined = undefined
  7. const h: symbol = Symbol()

标准库就是内置对象所对应的声明

Object类型

并不是单纯指object类型,而是指所有的非原始数据类型

  • 对象
  • 数组
  • 函数 ```typescript const foo: object = // function() // [] // {}

// 限制对象类型 const obj: {} = {}

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

  1. <a name="DajsZ"></a>
  2. ### 数组类型
  3. ```typescript
  4. const arr:Array<number> = [1, 2, 3]
  5. const arr1: number[] = [1, 2, 3]
  6. ---------------------------
  7. function sum( ...args: number[]) {
  8. // 判断是不是每个成员都是数字
  9. return args.reduce((prev,current) => prev + current, 0)
  10. }
  11. sum(1, 2, 3)
  12. // sum(1, 2, 3, "4") // 报错

元组类型

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

// const age = tuple[0]

// const name = tulpe[1]

const [age, name] = tuple

// ------------
Object.entries({
  foo: 123,
  bar: 456
})

枚举类型 (enum)

const PostStatus = {
  Draft: 0,
  Unpublished: 1
  Published: 2
}

const enum PostStatus {
  Draft = 0,
  Unpublished = 1
  Published = 2
}

const post = {
  title: "hello typeScript",
  content: "typeScript is a typed support of javaScript",
  status: PostStatus.Draft   // 0 , 1,  2
}

函数类型 (Function Types)

function func1 (a, b) {
   return "func1"
}

// ts:
// 传入参数个数 为2 类型为number 返回值 string
function func1 (a: number. b: number) : string {
  return "func1"
}

// 传入参数个数 为1 或2, 第二个形参可以不传  类型为number 返回值 string
function func1 (a: number. b?: number) : string {
  return "func1"
}

或者:
function func1 (a: number. b: number = 10) : string {
  return "func1"
}

// 接收任意个数的参数 ...rest
function func1 (a: number. b: number = 10, ...rest: number[]) : string {
  return "func1"
}

// 箭头函数的方式:
const func2: (a: number, b: number) => string = {
  return "func2"
}

任意类型 (Any Types)

function stringify(value: any) {
  return JSON.stringify(value)
}
// 轻易不要使用any

隐式类型推断

let age = 18 // number
age = "string" // 会报错

类型断言

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

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

// 断言
// 类型断言不是类型转换
const num1 = res as number;

const num2 = <numebr>res

接口 interface

接口就是用来约束对象的结构,一个对象去实现一个接口, 必须去拥有这个接口当中所约束的所有成员

// 定义接口、

interface Post {
  title: string
  content: string
}

function printPost(post: Post) {
  console.log(post.title)
  console.log(post.content)
  subtitle?: string // 可选成员
  readonly summary: string // 只读成员
}

// 即约束函数的参数必须是对象,且有title和content两个key值
printPost({
  title: "dsffd",
  content: 'sdfsdfdf'
})

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

const cache: Cache = {}

cache.foo = "v1"
cache.bar = "v2"

描述一类具体事物的抽象特征

  • es5:函数 + 原型 模拟实现类
  • es6:s6开始javaScript中有个专门的class
  • typeScript 增强了 class 的相关语法
  • 访问修饰符
    • private 私有属性 、只允许父类中使用
    • public 共有成员,默认
    • protected 受保护属性、只允许子类中使用
  • 只读属性

    • readonly ```typescript class Person { name: string private age: number protected gender: boolean protected readonly dontchange: boolean // 子类和父类都不允许修改

    constructor (name: string, age: number) { this.name = name this.age = age }

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

const tom = new Person(‘tom’, 18) console.log(tom.name) console.log(tom.age) // 访问不到 console.log(tom.gender) // 访问不到, 只允许在子类中访问的


- 类与接口
   - implements
```typescript
interface EatAndRun {
  eat(food: string): void
  run(distance: string): void
}

class Person implements EatAndRun {
  eat(food: string): void {
    console(`优雅的进餐 ${food}`)
  }

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

}

class Animal implements EatAndRun {
  eat(food: string): void {
    console(`呼噜呼噜的吃 ${food}`)
  }

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

}

// 改进:
interface Eat {
  eat(food: string): void
}
interface Run {
  run(distance: string): void
}


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

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

}
  • 抽象类
    • class 关键词前面添加 abstract
    • 被定义为抽象类之后 只能被继承, 不能够再使用new的方式去创建实例了
    • 父类中有抽象方法时,子类就必须去实现这个方法 ``typescript abstract class Animal { eat (food: string): void { console.log(呼噜呼噜的吃${food}`) } // 定义抽象方法,子类继承的时候也必须定义该方法 abstract run (distance: number): void }

class Dog extends Animal { run(distance: number) { console(四脚爬行 ${distance}) } }

const dog = new Dog() dog.eat(‘阿西吧’) dog.run(100)


<a name="K93bf"></a>
### 泛型

- 定义函数接口或类的时候没有去指定具体的类型, 等到使用的时候再去指定具体类型的
```typescript
function creatNumberArray (length: number, value: number): number[] {
  const arr = Array<number>(length).fill(value)
  return arr
}
function creatNumberString (length: number, value: string): string[] {
  const arr = Array<string>(length).fill(value)
  return arr
}
const res = creatNumberArray(3, 100)
// [100, 100, 100]

const res = creatNumberString(3, 'foo')
// ['foo', 'foo', 'foo']


// 使用泛型
function creatArray (length: number, value: T): T[] {
  const arr = Array<T>(length).fill(value)
  return arr
}

const res = creatArray<string>(3, 'foo')
// ['foo', 'foo', 'foo']
const res = creatArray<number>(3, 'foo')
// [100, 100, 100]

类型声明

引入第三方库时候

  • 可以去安装对应库的类型声明模块
  • 也可以的时候用 declare ```typescript import { camelCase } from ‘lodash’

declare function camelCase (input: string): string

const res = camelCase(‘hello typed’)

```