javascript强类型入门——TypeScript

日期:2018-07-15 23:20作者:Bougie

上个月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
    
  2. 数组、元组

    // 数组
    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'
    
  3. 枚举类型

    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

  4. 任意类型

    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' 
    
  5. 空类型

    // 无返回值的函数为Void类型
    function returnNothing(): void {
     console.log("I return nothing")
    }
    
  6. null和undefined

    // 默认情况下null和undefined是所有类型的子类型
    let u: undefined = undefined
    let n: null = null
    let s: string = 'str'
    let m: number = 1
    s = u
    m = n
    
  7. 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);
    }
    
  2. 变量的结构与赋值

    // 基本用法与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];
    

    可见可读性增强很多

  3. 函数默认值

    // 函数默认值设置
    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;
    }
    
  4. 展开操作符
    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);
    
  2. 可选属性

    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" });
    
  3. 只读属性

    interface Point {
     readonly x: number;
     readonly y: number;
    }
    // 定义只读属性
    let p1: Point = { x: 10, y: 20 };
    // 只读的Array
    let ro: ReadonlyArray = [1,2,3,4];
    
  4. 函数类型

    // 函数类型
    interface SearchFunc {
     // 参数类型与返回类型
     (source: string, subString: string): boolean;
    }
    let mySearch: SearchFunc = function(source: string, subString: string) {
    let result = source.search(subString);
    return result > -1;
    }
    
  5. 可索引类型

    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];
    
  6. 类类型接口

    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;
    }());
    
  7. 接口继承

    interface Shape {
     color: string;
    }
    interface PenStroke {
     penWidth: number;
    }
    interface Square extends Shape, PenStroke {
     sideLength: number;
    }
    //一个接口可以继承多个接口,创建出多个接口的合成接口。
    let square = {};
    square.color = "blue";
    square.sideLength = 10;
    square.penWidth = 5.0;
    
  8. 混合类型

    interface Counter {
     (start: number): string;
     interval: number;
     reset(): void;
    }
    function getCounter(): Counter {
     let counter = function (start: number) { };
     counter.interval = 123;
     counter.reset = function () { };
     return counter;
    }
    let counter = getCounter();
    counter(10);
    counter.reset();
    counter.interval = 5.0;
    
  9. 接口继承类

    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,今天就先到这里了。感觉这东西是不是有些强过头了,。也许使用这东西,二八法则就好。