• TypeScript

      • TypeScript大大提高了代码的可靠程度
      • Javascript自有类型系统的问题
    • 强类型与弱类型

      • 强类型与弱类型(类型安全)

        • 强类型: 语言层面限制函数的实参类型必须与形参数类型相同
        • 弱类型: 不限制,允许任意的数据隐式类型转换
      • 静态类型与动态类型(类型检查)

        • 静态类型: 变量声明时,类型就是明确的,并不能修改
        • 动态类型: 运行阶段才能明确变量类型,并能随时改变
    • js弱且动

      • 缺失了类型系统的可靠性
      • 是脚本语言,没有编译环节
    • 弱类型的问题

      1. setTimeout(() => {
      2. obj.foo() //此处有问题,需要等执行到才能发现
      3. }, 1000000)
      4. console.log(100+'100');
      5. // 100100
      6. // 3. 对象索引器的错误用法
      7. const obj = {}
      8. obj[true] = 100 // 属性名会自动转换为字符串
      9. console.log(obj['true'])
    • 强类型的优势

      1. // 强类型的优势
      2. // 1. 强类型代码错误更早暴露
      3. // 2. 强类型代码更智能,编码更准确
      4. // function render (element) {
      5. // element.className = 'container'
      6. // element.innerHtml = 'hello world'
      7. // }
      8. // =================================
      9. // 3. 重构更可靠
      10. // const util = {
      11. // aaa: () => {
      12. // console.log('util func')
      13. // }
      14. // }
      15. // =================================
      16. // 4. 减少了代码层面的不必要的类型判断
      17. function sum (a, b) {
      18. if (typeof a !== 'number' || typeof b !== 'number') {
      19. throw new TypeError('arguments must be a number')
      20. }
      21. return a + b
      22. }
    • Flow

      • JavaScript 的类型检查器
        function sum (a: number, b: number) {
        return a + b
        }

      • 安装: yarn add flow-bin —dev

      • 运行: yarn flow

      • 初始化配置文件: flow init

      • 退出: flow stop

      • 移除编译注解

        • 安装:yarn add flow-remove-types —dev
        • 移除某目录并移动到某目录 (a: number, b: number)

          • yarn flow-remove-types . -d dist
        • 编译工具 babel 配合插件也能实现自动移除编译注解
        • 安装 yarn add @babel/core (babel核心模块) @babel/cli (babel的cli工具,直接使用babel命令编译) @babel/preset-flow (转换flow类型的插件) —dev
        • 添加babel配置文件 .babelrc

          • {
            “presets”: [“@babel/preset-flow”]
            }
          • yarn babel src -d dist 把src下的目录文件都编译到dist目录当中
      • flow类型注解 ```javascript /**
      • 类型注解 *
      • @flow */

      function square (n: number) { return n * n }

      let num: number = 100

      // num = ‘string’ // error

      function foo (): number { return 100 // ok // return ‘string’ // error }

      function bar (): void { // return undefined } ```

    1. - Flow 原始类型
    1. /**
    2. * 原始类型
    3. *
    4. * @flow
    5. */
    6. const a: string = 'foobar'
    7. const b: number = Infinity // NaN // 100
    8. const c: boolean = false // true
    9. const d: null = null
    10. const e: void = undefined
    11. const f: symbol = Symbol()
    1. - Flow 数组类型
    1. /**
    2. * 数组类型
    3. *
    4. * @flow
    5. */
    6. const arr1: Array<number> = [1, 2, 3]
    7. const arr2: number[] = [1, 2, 3]
    8. // 元组
    9. const foo: [string, number] = ['foo', 100]
    1. - Flow 数组类型
    1. /**
    2. * 对象类型
    3. *
    4. * @flow
    5. */
    6. const obj1: { foo: string, bar: number } = { foo: 'string', bar: 100 }
    7. const obj2: { foo?: string, bar: number } = { bar: 100 } //foo?可有可无
    8. const obj3: { [string]: string } = {} //键和值,必须是字符串
    9. obj3.key1 = 'value1'
    10. obj3.key2 = 'value2'
    1. - Flow 数组类型
    1. /**
    2. * 函数类型
    3. *
    4. * @flow
    5. */
    6. function foo (callback: (string, number) => void) {
    7. callback('string', 100)
    8. }
    9. foo(function (str, n) {
    10. // str => string
    11. // n => number
    12. })
    1. - Flow 数组类型
    1. /**
    2. * 特殊类型
    3. *
    4. * @flow
    5. */
    6. // 字面量类型
    7. const a: 'foo' = 'foo' //必须是某值
    8. const type: 'success' | 'warning' | 'danger' = 'success' //三选一
    9. // ------------------------
    10. // 声明类型
    11. type StringOrNumber = string | number
    12. const b: StringOrNumber = 'string' // 100
    13. // ------------------------
    14. // Maybe 类型
    15. const gender: ? number = undefined
    16. // 相当于
    17. // const gender: number | null | void = undefined
    1. - Flow Mixed Any
    1. /**
    2. * Mixed Any
    3. *
    4. * @flow
    5. */
    6. // string | number | boolean | ....
    7. function passMixed (value: mixed) {
    8. if (typeof value === 'string') {
    9. value.substr(1)
    10. }
    11. if (typeof value === 'number') {
    12. value * value
    13. }
    14. }
    15. passMixed('string')
    16. passMixed(100)
    17. // ---------------------------------
    18. function passAny (value: any) {
    19. value.substr(1)
    20. value * value
    21. }
    22. passAny('string')
    23. passAny(100)
    • Flow 运行环境 API ```javascript /**
    • 运行环境 API *
    • @flow */

      const element: HTMLElement | null = document.getElementById(‘app’) ```

    • Flow 类型小结

    • JavaScript的超集(superset)

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

    • TypeScript作为一门编程语言,功能更为强在,生态也更健全、更完善

    • 前端领域中的第二语言,长周期的大项目,会建议选择TypeScript

    • 缺点一:语言本身多了很多概念,属于“渐进式”

    • 缺点二:项目初期,TypeScript会增加一些成本

    • 安装:

      • 先初始化一个项目中使用的package.json文件,管理项目依赖项: yarn init —yes
      • 添加TypeScript模块: yarn add typescript —dev
      • tsc编译某文件: yarn tsc XXX.ts 可生成编译后es2013的js文件
      • 配置文件,编译整个项目/工程
      • 创建typescript的配置文件: yarn tsc —init
      • 创建src目录,并把ts文件放入src
      • 然后运行 yarn tsc 即可编译
    • 原始数据类型

      1. // 原始数据类型
      2. const a: string = 'foobar'
      3. const b: number = 100 // NaN Infinity
      4. const c: boolean = true // false
      5. // 在非严格模式(strictNullChecks)下,
      6. // string, number, boolean 都可以为空
      7. // const d: string = null
      8. // const d: number = null
      9. // const d: boolean = null
      10. const e: void = undefined
      11. const f: null = null
      12. const g: undefined = undefined
      13. // Symbol 是 ES2015 标准中定义的成员,
      14. // 使用它的前提是必须确保有对应的 ES2015 标准库引用
      15. // 也就是 tsconfig.json 中的 lib 选项必须包含 ES2015
      16. const h: symbol = Symbol()
      17. //解决办法 "target": "ES2015", 或 "lib": [ES2015],
      18. // Promise
      19. // const error: string = 100
      20. //标准库就是内置对象所对应的声明
    • TypeScript 中文错误信息

      • yarn tsc —locale zh-CN
    • 作用域问题

      1. // 作用域问题
      2. // 默认文件中的成员会作为全局成员
      3. // 多个文件中有相同成员就会出现冲突
      4. // const a = 123
      5. // 解决办法1: IIFE 提供独立作用域
      6. // (function () {
      7. // const a = 123
      8. // })()
      9. // 解决办法2: 在当前文件使用 export,也就是把当前文件变成一个模块
      10. // 模块有单独的作用域
      11. const a = 123
      12. export {}
    • 作用域问题

      1. // Object 类型
      2. export {} // 确保跟其它示例没有成员冲突
      3. // object 类型是指除了原始类型以外的其它类型
      4. const foo: object = function () {} // [] // {}
      5. // 如果需要明确限制对象类型,则应该使用这种类型对象字面量的语法,或者是「接口」
      6. const obj: { foo: number, bar: string } = { foo: 123, bar: 'string' }
      7. // 接口的概念后续介绍
    • 数组类型

      1. // 数组类型
      2. export {} // 确保跟其它示例没有成员冲突
      3. // 数组类型的两种表示方式
      4. const arr1: Array<number> = [1, 2, 3]
      5. const arr2: number[] = [1, 2, 3]
      6. // 案例 -----------------------
      7. // 如果是 JS,需要判断是不是每个成员都是数字
      8. // 使用 TS,类型有保障,不用添加类型判断
      9. function sum (...args: number[]) {
      10. return args.reduce((prev, current) => prev + current, 0)
      11. }
      12. sum(1, 2, 3) // => 6
    • 元组(Tuple)类型

      1. // 元组(Tuple)
      2. export {} // 确保跟其它示例没有成员冲突
      3. const tuple: [number, string] = [18, 'zce']
      4. // const age = tuple[0]
      5. // const name = tuple[1]
      6. const [age, name] = tuple
      7. // ---------------------
      8. const entries: [string, number][] = Object.entries({
      9. foo: 123,
      10. bar: 456
      11. })
      12. const [key, value] = entries[0]
      13. // key => foo, value => 123
    • 枚举类型

      1. // 枚举(Enum)
      2. export {} // 确保跟其它示例没有成员冲突
      3. // 用对象模拟枚举
      4. // const PostStatus = {
      5. // Draft: 0,
      6. // Unpublished: 1,
      7. // Published: 2
      8. // }
      9. // 标准的数字枚举
      10. // enum PostStatus {
      11. // Draft = 0,
      12. // Unpublished = 1,
      13. // Published = 2
      14. // }
      15. // 数字枚举,枚举值自动基于前一个值自增
      16. // enum PostStatus {
      17. // Draft = 6,
      18. // Unpublished, // => 7
      19. // Published // => 8
      20. // }
      21. // 字符串枚举
      22. // enum PostStatus {
      23. // Draft = 'aaa',
      24. // Unpublished = 'bbb',
      25. // Published = 'ccc'
      26. // }
      27. // 常量枚举,不会侵入编译结果
      28. const enum PostStatus {
      29. Draft,
      30. Unpublished,
      31. Published
      32. }
      33. const post = {
      34. title: 'Hello TypeScript',
      35. content: 'TypeScript is a typed superset of JavaScript.',
      36. status: PostStatus.Draft // 3 // 1 // 0
      37. }
      38. // PostStatus[0] // => Draft
    • 函数类型

      1. // 函数类型
      2. export {} // 确保跟其它示例没有成员冲突
      3. function func1 (a: number, b: number = 10, ...rest: number[]): string {
      4. return 'func1'
      5. }
      6. func1(100, 200)
      7. func1(100)
      8. func1(100, 200, 300)
      9. // -----------------------------------------
      10. const func2: (a: number, b: number) => string = function (a: number, b: number): string {
      11. return 'func2'
      12. }
    • 任意类型

      1. // 任意类型(弱类型)
      2. export {} // 确保跟其它示例没有成员冲突
      3. function stringify (value: any) {
      4. return JSON.stringify(value)
      5. }
      6. stringify('string')
      7. stringify(100)
      8. stringify(true)
      9. let foo: any = 'string'
      10. foo = 100
      11. foo.bar()
      12. // any 类型是不安全的
    • 隐式类型推断

      1. // 隐式类型推断
      2. export {} // 确保跟其它示例没有成员冲突
      3. let age = 18 // 已经推断了为number
      4. // age = 'string' //不能用 了
      5. let foo //推断为any
      6. foo = 100
      7. foo = 'string'
      8. // 建议为每个变量添加明确的类型标注
    • 类型断言

      1. // 类型断言
      2. export {} // 确保跟其它示例没有成员冲突
      3. // 假定这个 nums 来自一个明确的接口
      4. const nums = [110, 120, 119, 112]
      5. const res = nums.find(i => i > 0)
      6. // const square = res * res
      7. const num1 = res as number
      8. const num2 = <number>res // JSX 下不能使用
    • 接口

      1. // 接口
      2. export {} // 确保跟其它示例没有成员冲突
      3. interface Post {
      4. title: string
      5. content: string
      6. } //接口就是用来约束对象的结构
      7. function printPost (post: Post) {
      8. console.log(post.title)
      9. console.log(post.content)
      10. }
      11. printPost({
      12. title: 'Hello TypeScript',
      13. content: 'A javascript superset'
      14. })
    1. // 可选成员、只读成员、动态成员
    2. export {} // 确保跟其它示例没有成员冲突
    3. // -------------------------------------------
    4. interface Post {
    5. title: string
    6. content: string
    7. subtitle?: string
    8. readonly summary: string
    9. }
    10. const hello: Post = {
    11. title: 'Hello TypeScript',
    12. content: 'A javascript superset',
    13. summary: 'A javascript'
    14. }
    15. // hello.summary = 'other'
    16. // ----------------------------------
    17. //动态键值
    18. interface Cache {
    19. [prop: string]: string
    20. }
    21. const cache: Cache = {}
    22. cache.foo = 'value1'
    23. cache.bar = 'value2'
    • 类型断言

      1. // 类(Class)
      2. export {} // 确保跟其它示例没有成员冲突
      3. class Person {
      4. name: string // = 'init name'
      5. age: number
      6. constructor (name: string, age: number) {
      7. this.name = name
      8. this.age = age
      9. }
      10. sayHi (msg: string): void {
      11. console.log(`I am ${this.name}, ${msg}`)
      12. }
      13. }
    • 类的访问修饰符

      1. // 类的访问修饰符
      2. export {} // 确保跟其它示例没有成员冲突
      3. class Person {
      4. public name: string // = 'init name'
      5. private age: number
      6. protected gender: boolean
      7. constructor (name: string, age: number) {
      8. this.name = name
      9. this.age = age
      10. this.gender = true
      11. }
      12. sayHi (msg: string): void {
      13. console.log(`I am ${this.name}, ${msg}`)
      14. console.log(this.age)
      15. }
      16. }
      17. class Student extends Person {
      18. private constructor (name: string, age: number) {
      19. super(name, age)
      20. console.log(this.gender)
      21. }
      22. static create (name: string, age: number) {
      23. return new Student(name, age)
      24. }
      25. }
      26. const tom = new Person('tom', 18)
      27. console.log(tom.name)
      28. // console.log(tom.age)
      29. // console.log(tom.gender)
      30. const jack = Student.create('jack', 18)
    • 类的访问修饰符

      1. // 类的只读属性
      2. export {} // 确保跟其它示例没有成员冲突
      3. class Person {
      4. public name: string // = 'init name'
      5. private age: number
      6. // 只读成员
      7. protected readonly gender: boolean
      8. constructor (name: string, age: number) {
      9. this.name = name
      10. this.age = age
      11. this.gender = true
      12. }
      13. sayHi (msg: string): void {
      14. console.log(`I am ${this.name}, ${msg}`)
      15. console.log(this.age)
      16. }
      17. }
      18. const tom = new Person('tom', 18)
      19. console.log(tom.name)
      20. // tom.gender = false
    • 类与接口

      1. // 类与接口
      2. export {} // 确保跟其它示例没有成员冲突
      3. interface Eat {
      4. eat (food: string): void
      5. }
      6. interface Run {
      7. run (distance: number): void
      8. }
      9. class Person implements Eat, Run {
      10. eat (food: string): void {
      11. console.log(`优雅的进餐: ${food}`)
      12. }
      13. run (distance: number) {
      14. console.log(`直立行走: ${distance}`)
      15. }
      16. }
      17. class Animal implements Eat, Run {
      18. eat (food: string): void {
      19. console.log(`呼噜呼噜的吃: ${food}`)
      20. }
      21. run (distance: number) {
      22. console.log(`爬行: ${distance}`)
      23. }
      24. }
    • 抽像类

      1. // 抽像类
      2. export {} // 确保跟其它示例没有成员冲突
      3. abstract class Animal {
      4. eat (food: string): void {
      5. console.log(`呼噜呼噜的吃: ${food}`)
      6. }
      7. abstract run (distance: number): void
      8. }
      9. class Dog extends Animal {
      10. run(distance: number): void {
      11. console.log('四脚爬行', distance)
      12. }
      13. }
      14. const d = new Dog()
      15. d.eat('嗯西马')
      16. d.run(100)
    • 泛型

      1. // 泛型
      2. export {} // 确保跟其它示例没有成员冲突
      3. function createNumberArray (length: number, value: number): number[] {
      4. const arr = Array<number>(length).fill(value)
      5. return arr
      6. }
      7. function createStringArray (length: number, value: string): string[] {
      8. const arr = Array<string>(length).fill(value)
      9. return arr
      10. }
      11. function createArray<T> (length: number, value: T): T[] {
      12. const arr = Array<T>(length).fill(value)
      13. return arr
      14. }
      15. // const res = createNumberArray(3, 100)
      16. // res => [100, 100, 100]
      17. const res = createArray<string>(3, 'foo')
    • 泛型

      1. // 类型声明
      2. //安装 yarn add lodash
      3. //npm install @types/lodash
      4. import { camelCase } from 'lodash'
      5. import qs from 'query-string'
      6. qs.parse('?key=value&key2=value2')
      7. // declare function camelCase (input: string): string
      8. const res = camelCase('hello typed')