javascript强类型入门——TypeScript

作者: Bougie 创建于: 2018-07-15 | 成长

javascript强类型入门——TypeScript

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

上次更新: 2019-4-13 00:24:55