javascript强类型入门——TypeScript

V3hlm8.png

上个月 TypeScript 挤进了编程语言排行榜的前五十名。已成为主修 JS 的同学们不可忽视的一门语言。由于 ES6 出现以来 js 已发生翻天覆地的变化,这篇文章主要从 ES6 的角度来一步步阐述 TypeScript。

# 安装

学习期间建议搭配gulp食用:

npm i gulp gulp-typescript -D

gulpfile.js配置:

const gulp = require('gulp')
const ts = require('gulp-typescript')
gulp.task('tsc', function () {
  let tsResult = gulp.src('src/**/*.ts').pipe(
    ts({
      noImplicitAny: true
    })
  )
  return tsResult.js.pipe(gulp.dest('dist'))
})
gulp.task('dev', function () {
  gulp.watch('src/**/*.ts', ['tsc'])
})

package.json中添加:

{
  "scripts": {
    "dev": "gulp dev"
  }
}

运行:

npm run dev

# 基本类型

  1. 布尔值、数字、字符串
let bol: boolean
let num: number
let str: string
  1. 数组、元组
// 数组
let list1: number[] = [1, 2, 3]
let list2: Array<number> = [1, 2, 3]
// 多重数组
let list3: number[][][] = [[[1, 2, 3]]]
// 元组,初始化时需类型顺序赋值
let x: [string, number] = ['1', 1]
// 跨界后可任选两种类型的一种
x[3] = 'world'
  1. 枚举类型
enum Color {
  Red,
  Green,
  Blue
}
let c: Color = Color.Green
console.log(Color, c)
// 声明默认值
enum Colors {
  Red = 1,
  Green = 3,
  Blue = 4
}
let cs: Colors = Colors.Blue
console.log(Colors, cs)

⬇️ 编译后

var Color
;(function (Color) {
  Color[(Color['Red'] = 0)] = 'Red'
  Color[(Color['Green'] = 1)] = 'Green'
  Color[(Color['Blue'] = 2)] = 'Blue'
})(Color || (Color = {}))
var c = Color.Green
console.log(Color, c)
// 声明默认值
var Colors
;(function (Colors) {
  Colors[(Colors['Red'] = 1)] = 'Red'
  Colors[(Colors['Green'] = 3)] = 'Green'
  Colors[(Colors['Blue'] = 4)] = 'Blue'
})(Colors || (Colors = {}))
var cs = Colors.Blue
console.log(Colors, cs)

打印的结果为:

{ '0': 'Red', '1': 'Green', '2': 'Blue', Red: 0, Green: 1, Blue: 2 } 1
{ '1': 'Red', '3': 'Green', '4': 'Blue', Red: 1, Green: 3, Blue: 4 } 4

枚举类型的大概意思是用来生成一种双向的对象,既能根据key获取value,也能根据value获取key

  1. 任意类型
let notSure: any
// 可任意进行类型转换
notSure = 4
notSure = 'str'
// 数组,可为任意类型
let list: any[] = [1, true, 'free']
list[1] = 100
list[1] = 'str'
// 元组
let tuple: [any, number] = [true, 1]
// 首位可为任意类型
tuple[0] = 1
// 跨界后可为任意类型
tuple[2] = 'str'
  1. 空类型
// 无返回值的函数为Void类型
function returnNothing(): void {
  console.log('I return nothing')
}
  1. null 和 undefined
// 默认情况下null和undefined是所有类型的子类型
let u: undefined = undefined
let n: null = null
let s: string = 'str'
let m: number = 1
s = u
m = n
  1. never 类型
// 返回never的函数必须存在无法达到的终点
// 即会阻止程序向下运行
function error(message: string): never {
  throw new Error(message)
}
// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
  while (true) {}
}

# 变量申明

  1. 使用 let 和 const 代替 var
    体验块级作用域的好处
// 输出十个10
for (var i = 0; i < 10; i++) {
  setTimeout(function () {
    console.log(i)
  }, 100 * i)
}
// 输出0到9
for (let i = 0; i < 10; i++) {
  setTimeout(function () {
    console.log(i)
  }, 100 * i)
}

⬇️ 编译后

// 输出十个10
for (var i = 0; i < 10; i++) {
  setTimeout(function () {
    console.log(i)
  }, 100 * i)
}
var _loop_1 = function (i_1) {
  setTimeout(function () {
    console.log(i_1)
  }, 100 * i_1)
}
// 输出0到9
for (var i_1 = 0; i_1 < 10; i_1++) {
  _loop_1(i_1)
}
  1. 变量的结构与赋值
// 基本用法与ES6一样
// 传入Rest参数
let [first, ...rest] = [1, 2, 3, 4]
// 结构并重命名,这里不指定bar的类型会报错,不知道为什么
let { foo: customName }: { foo: string; bar: string } = {
  foo: '123',
  bar: '456'
}
// 变量交换,很奇怪这里不加分号会报错
let [a, b]: [string, string] = ['a', 'b']
;[b, a] = [a, b]

⬇️ 编译后

// 基本用法与ES6一样
// 传入Rest参数
var _b = [1, 2, 3, 4],
  first = _b[0],
  rest = _b.slice(1)
// 结构并重命名,这里不指定bar的类型会报错,不知道为什么
var customName = { foo: '123', bar: '456' }.foo
// 变量交换,很奇怪这里不加分号会报错
var _c = ['a', 'b'],
  a = _c[0],
  b = _c[1]
;(_a = [a, b]), (b = _a[0]), (a = _a[1])

可见可读性增强很多

  1. 函数默认值
// 函数默认值设置
function keepWholeObject(wholeObject: { a: string; b?: number }) {
  let { a, b = 1001 } = wholeObject
}
function f({ a, b } = { a: '', b: 0 }): void {
  // ...
}

⬇️ 编译后

// 函数默认值设置
function keepWholeObject(wholeObject) {
  var a = wholeObject.a,
    _a = wholeObject.b,
    b = _a === void 0 ? 1001 : _a
}
function f(_a) {
  var _b = _a === void 0 ? { a: '', b: 0 } : _a,
    a = _b.a,
    b = _b.b
  // ...
}

这个与 ES6 的方法不太一样,在 ES6 我们一般:

function fn({ a = 'a', b = 'b' }) {}

babel 编译后

'use strict'
function fn(_ref) {
  var _ref$a = _ref.a,
    a = _ref$a === undefined ? 'a' : _ref$a,
    _ref$b = _ref.b,
    b = _ref$b === undefined ? 'b' : _ref$b
}
  1. 展开操作符
let arr1 = [1, 2]
let arr2 = [3, 4]
let arr3 = [0, ...arr1, ...arr2, 5]
let obj1 = { food: 'spicy', price: '$', ambiance: 'noisy' }
let ojb2 = { ...obj1, food: 'rich' }

⬇️ 编译后

var arr1 = [1, 2]
var arr2 = [3, 4]
var arr3 = [0].concat(arr1, arr2, [5])
// 这里相当于加了个polyfill
var __assign =
  (this && this.__assign) ||
  Object.assign ||
  function (t) {
    for (var s, i = 1, n = arguments.length; i < n; i++) {
      s = arguments[i]
      for (var p in s)
        if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]
    }
    return t
  }
var obj1 = { food: 'spicy', price: '$', ambiance: 'noisy' }
var ojb2 = __assign({}, obj1, { food: 'rich' })

# 接口

  1. 什么是接口(interface)
interface LabelledValue {
  label: string
}
// 接口用来定义一种类型,用法同基本类型
function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label)
}
let myObj = { size: 10, label: 'Size 10 Object' }
printLabel(myObj)
  1. 可选属性
interface SquareConfig {
  color?: string
  width?: number
}
// 定义可选属性
function createSquare(config: SquareConfig): SquareConfig {
  let newSquare = { color: 'white', area: 100 }
  if (config.color) {
    newSquare.color = config.color
  }
  if (config.width) {
    newSquare.area = config.width * config.width
  }
  return newSquare
}
let mySquare = createSquare({ color: 'black' })
  1. 只读属性
interface Point {
  readonly x: number
  readonly y: number
}
// 定义只读属性
let p1: Point = { x: 10, y: 20 }
// 只读的Array
let ro: ReadonlyArray<number> = [1, 2, 3, 4]
  1. 函数类型
// 函数类型
interface SearchFunc {
  // 参数类型与返回类型
  (source: string, subString: string): boolean
}
let mySearch: SearchFunc = function (source: string, subString: string) {
  let result = source.search(subString)
  return result > -1
}
  1. 可索引类型
interface StringArray {
  [index: number]: string
}
let myArray: StringArray = ['Bob', 'Fred']
let myArrayObj: StringArray = {
  1: 'Bob',
  2: 'Fred'
}
// 索引必须为数字
let myStr: string = myArray[0]
let myObjStr: string = myArrayObj[1]
  1. 类类型接口
interface ClockInterface {
  currentTime: Date
  setTime(d: Date): void
}
// 类类型接口
class Clock implements ClockInterface {
  currentTime: Date
  setTime(d: Date) {
    this.currentTime = d
  }
  constructor(h: number, m: number) {}
}

⬇️ 编译后

var Clock = /** @class */ (function () {
  // currentTime: Date;
  function Clock(h, m) {
    this.currentTime = new Date(h + m)
  }
  return Clock
})()
  1. 接口继承
interface Shape {
  color: string
}
interface PenStroke {
  penWidth: number
}
interface Square extends Shape, PenStroke {
  sideLength: number
}
//一个接口可以继承多个接口,创建出多个接口的合成接口。
let square = <Square>{}
square.color = 'blue'
square.sideLength = 10
square.penWidth = 5.0
  1. 混合类型
interface Counter {
  (start: number): string
  interval: number
  reset(): void
}
function getCounter(): Counter {
  let counter = <Counter>function (start: number) {}
  counter.interval = 123
  counter.reset = function () {}
  return counter
}
let counter = getCounter()
counter(10)
counter.reset()
counter.interval = 5.0
  1. 接口继承类
class Control {
  private state: any
}
interface SelectableControl extends Control {
  select(): void
}
class Button extends Control implements SelectableControl {
  select() {}
}
class TextBox extends Control {
  select() {}
}

⬇️ 编译后

var Control = /** @class */ (function () {
  function Control() {}
  return Control
})()
// 此处有个实现类继承的polyfill
var __extends =
  (this && this.__extends) ||
  (function () {
    var extendStatics =
      Object.setPrototypeOf ||
      ({ __proto__: [] } instanceof Array &&
        function (d, b) {
          d.__proto__ = b
        }) ||
      function (d, b) {
        for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]
      }
    return function (d, b) {
      extendStatics(d, b)
      function __() {
        this.constructor = d
      }
      d.prototype =
        b === null ? Object.create(b) : ((__.prototype = b.prototype), new __())
    }
  })()
var Button = /** @class */ (function (_super) {
  __extends(Button, _super)
  function Button() {
    return (_super !== null && _super.apply(this, arguments)) || this
  }
  Button.prototype.select = function () {}
  return Button
})(Control)
var TextBox = /** @class */ (function (_super) {
  __extends(TextBox, _super)
  function TextBox() {
    return (_super !== null && _super.apply(this, arguments)) || this
  }
  TextBox.prototype.select = function () {}
  return TextBox
})(Control)

2018-07-15,今天就先到这里了。感觉这东西是不是有些强过头了,。也许使用这东西,二八法则就好。