拉勾大前端培训--TypeScript

158 阅读7分钟
  1. 强类型与弱类型
  • a. 强类型不允许随意的隐式类型转换,弱类型是允许的
  1. 静态类型与动态类型
  • a. 静态类型:声明时它的类型就是明确的
  • b. 动态类型:变量是没有类型的,运行的时候确定
  1. 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
 */

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

const arr2: number[] = [1, 2, 3]

// 元组
const foo: [string, number] = ['foo', 100]
  • 对象类型:
/**
 * 对象类型
 *
 * @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 = 'value1'
obj3.key2 = 'value2'
  • 函数类型
/**
 * 函数类型
 *
 * @flow
 */

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

foo(function (str, n) {
  // str => string
  // n => number
})
  • 特殊类型
/**
 * 特殊类型
 *
 * @flow
 */

// 字面量类型

const a: 'foo' = 'foo'

const type: 'success' | 'warning' | 'danger' = 'success'

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

// 声明类型

type StringOrNumber = string | number

const b: StringOrNumber = 'string' // 100

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

// Maybe 类型

const gender: ?number = undefined
// 相当于
// const gender: number | null | void = undefined
  • a. 类型注解
/**
 * 类型注解
 *
 * @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
}
  • b. 本身不是JavaScript语法,所以编译的时候不支持
  • c. 通过flow-remove-types可以移除
  • d. 或者通过babel移除
  • e. 可以给原始数据类型和数组、对象、函数类型、特殊类型添加类型注解
  1. TypeScript
  • a. JavaScript的超集
    • 原始数据类型
// 原始数据类型

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
  • b. 存在作用域问题
// 作用域问题

// 默认文件中的成员会作为全局成员
// 多个文件中有相同成员就会出现冲突
// const a = 123

// 解决办法1: IIFE 提供独立作用域
// (function () {
//   const a = 123
// })()

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

export {}
  • c. :object指代的不只是对象类型,如果需要指代对象类型,需要用{}字面量
// Object 类型

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

// object 类型是指除了原始类型以外的其它类型
const foo: object = function () {} // [] // {}

// 如果需要明确限制对象类型,则应该使用这种类型对象字面量的语法,或者是「接口」
const obj: { foo: number, bar: string } = { foo: 123, bar: 'string' }
  • d. 数组:Array<>尖括号中指的是数组中存的内容类型,同样用number[]也可以代表数字数组
// 数组类型

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

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

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
  • e. 元组:固定长度的数组
// 元组(Tuple)

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

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
  • f. 枚举:使用对象模拟这种数据类型,ts中可以通过enum关键字定义,最终会编译成双向的键值对对象
// 枚举(Enum)

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

// 用对象模拟枚举
// 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
  • g. 函数类型:
    • ⅰ. 函数声明:参数类型设置在参数后面,返回值类型设置在参数括号的后面。如果参数后面加一个问好,那就编程可选参数了。如果需要传入任意个数的参数,需要用到...rest: 后面加具体类型
    • ⅱ. 函数表达式:函数通过变量方式赋值
// 函数类型

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

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'
}
  • h. 任意类型::any是可以接收任意类型的参数。是不安全的,不建议使用
// 任意类型(弱类型)

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

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

stringify('string')

stringify(100)

stringify(true)

let foo: any = 'string'

foo = 100

foo.bar()

// any 类型是不安全的
  • i. 隐式类型推断:let age = 18ts就会推断age是number数据类型,如果传入的是string就会报错。建议为每个变量添加明确的类型
// 隐式类型推断

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

let age = 18 // number

// age = 'string'

let foo

foo = 100

foo = 'string'

// 建议为每个变量添加明确的类型标注
  • j. 类型断言:使用as语法或者在变量前使用<>
// 类型断言

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

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

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

// const square = res * res

const num1 = res as number

const num2 = <number>res // JSX 下不能使用
  • k. 接口:一种规范或者一种契约。使用interface关键词,后面接上接口名称{}。约束对象的结构。接口中可以添加可选成员,在成员名称后面添加一个?。接口中也可以添加只读成员,通过前面添加readonly关键词修饰。动态成员:成员使用[pop: string]这种方式创建。
// 接口

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

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'
})
  • l. 类: ⅰ.
// 类(Class)

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

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:默认是public,protected受保护的:只允许在子类访问的成员。可以控制类成员的访问级别。
    • ⅲ. 只读属性:在成员前添加readonly
    • ⅳ. 类与接口:首先在接口中抽象对应的方法,然后在类中具体去实现对应的方法。建议一个接口抽象单独的一个方法。
    • ⅴ. 抽象类:在类的前面添加abstract,就会将此类定义为抽象类。定义为抽象类之后只能继承,不能通过new关键字创建了。
  • m. 泛型:在声明的时候不去定义类型,在调用的时候传递一个类型。createArray
// 泛型
export {} // 确保跟其它示例没有成员冲突

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')
  • n. 类型声明:通过declare方式声明function。在安装第三方库的时候,有可能没有类型声明,如果没有的话需要单独安装一个类型声明模块。
// 类型声明

import { camelCase } from 'lodash'
import qs from 'query-string'

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

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

const res = camelCase('hello typed')