小讲Typescript

前言

  Typescript做为ECMA的实现,javascript的超集,目前已经普遍在项目中使用。typescript是什么?有什么具体功能?这些已经被你们写得差很少了。在这里,我再也不赘述ts的做用,而是直接用起来,从一个初学者角度告诉你们一些使用typescript的心得。下文将会完整地讲解一下typescript各个方面的具体用法,力求在我总结之余让你们有所收获。javascript

正文

  typescript是type+script(js)。它的本质是经过类型定义来限制js灵活多变的语法。ts提供了一些基本类型,但同时容许开发者自定义类型,在一些复杂状况下还可使用一些高级类型。在使用以前先总结几点能够帮助理解的要点:html

  • ts能够由tsc(Type Script Complier)编译器编译成js,项目的ts代码不管在开发时仍是打包后都会经历编译环节,最终执行的只会是js代码。
  • ts是js的超集,支持全部的JavaScript语法,支持es六、es7等新语法,不须要使用babel进行转换,在tsconfig.json配置文件中进行配置以后,tsc便能将其编译成指定版本的js代码。
  • ts能够经过tsconfig.json配置一些编译规则,违反这些规则是致使编译错误,可是即使编译错误不会打断编译,tsc仍是会继续完成编译。

开始使用

  1. 全局安装typescriptjava

    yarn global add typescript
    // 或使用 npm
    npm i typescript -g
    复制代码
  2. 使用tsc进行编译node

    // 使用默认的配置编译helloword.ts文件
    tsc helloword.ts 
    复制代码
  3. 使用tsconfig.json配置文件es6

    // 在根目录下创建tsconfig.json配置文件
    {
        // 编译选项
        "compilerOptions": {
            // 编译目标,如'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
            "target": "es5",
            // 编译代码使用的模块 'commonjs', 'amd', 'system', 'umd' or 'es2015'
            "module": "commonjs",
            // 输出文件夹,默认会对应ts文件下生成同名js文件
            "outDir": "./dist",
            // 类型声明文件的目录,工做目录下@types文件会自动加入
            "typeRoots": [],
        },
        // 进行编译的文件
        // 包含在files中的文件是必定会进行编译(即便使用exclude去除)
        "files": [
            "hello.ts",
            "word.ts"
        ],
        // 要编译的目录或文件
        "include": [
            "./folder"
        ],
        // 不要包含的目录或文件
        "exclude": [
            "./folder/**/*.spec.ts",
            "./folder/someSubFolder"
        ]
    }
    复制代码

    完整的配置选项能够参考 typescript官网typescript

    在创建好配置文件以后,直接执行tsc,编译好的js文件便会存在于js目录,当module为commonjs时,能够直接使用node进行执行测试。固然还可使用自动编译,在tsconfig.json顶层中加入 "compileOnSave": true,告诉IDE保存后进行编译,vscode按照typescript插件,以后执行 tsc -w,这样子以后就能进行自动编译。npm

基本类型

  ts提供了Boolean、Number、String、Array、Tuple、Enum、Any、Void、Null、Undefined、Never、Object 共12种基本类型。json

  1. Boolean、Number、String数组

    // 大部分类型的使用很是简单
    let bool: boolean = true;
    let num: number = 1213;
    let str: string = "Hello World";
    复制代码
  2. Array和Tuple(元组)bash

    // 数组类型
    let arr: number[] = [1213,2324]
    // 或者 
    const arr2: Array<string> = ['1213', '234234']
    
    // 元组为有限个有序的数组,元组须要对数组的每一个元素声明类型
    let tup: [string, number] = ["121", 2321];
    tup[0] = "wadsas";
    tup[0] = 1212 // 报错
    复制代码
  3. Enum类型

      枚举类型是一种很特殊的类型,其它的类型声明在编译成js代码以后便不会存在,但枚举类型是会编辑成js代码的。

    enum Tristate {
      False,
      True,
      Unknown
    }
    // 编译成js以后
    var Tristate;
    (function(Tristate) {
      Tristate[(Tristate['False'] = 0)] = 'False';
      Tristate[(Tristate['True'] = 1)] = 'True';
      Tristate[(Tristate['Unknown'] = 2)] = 'Unknown';
    })(Tristate || (Tristate = {}));
    // 也就是如下对象
    {0: "False", 1: "True", 2: "Unknown", False: 0, True: 1, Unknown: 2}
    
    let tri: Tristate = Tristate.False
    复制代码

    枚举选项的值默认是从0开始,上面的Tristate中False为0, True为1,Unknown为2,咱们能够改变枚举的值:

    enum HighColor {
      Green,
      Black = 5,
      Blue
    }
    // Green为0,Black为5,Blue为6
    // 字符串枚举
    enum Type {
      ON_LINE = "ON_LINE",
      DOWN = "DOWN",
      OTHER = "OTHER"
    }
    复制代码

    还可使用 enum + namespace 的声明的方式向枚举类型添加静态方法

    enum PersonType {
      young,
      middle,
      old
    }
    namespace PersonType {
      export function isOld(person: PersonType) {
        return person === PersonType.old;
      }
    }
    const Marry: PersonType = PersonType.old;
    console.log(PersonType.isOld(Marry));
    复制代码
  4. Any和Object

    Any是任意类型,当一个变量被声明为了any类型,它能够是任何值,就如js同样能够随意改变其值而不会有提示,当从js项目迁移到ts,any能够提供极大的便利,但一个项目内应该尽可能少地使用any类型,由于这意味着你放弃类型检查。

    let a: any = 1231;
    a = "12313";
    a = true
    复制代码

    Object是对象类型,它仅表示变量是对象类型,不会对对象的属性作出限制,一个比较适合的场景是Object.create(o: object)

    declare function create(o: object | null): void;
    create({ prop: 0 }); // OK
    create(null); // OK
    create(42); // Error
    
    let obj: object = {a: 12}
    obj = {b: 12, c: 88}
    obj.d = 1212 // 报错
    
    复制代码
  5. Void、Never、Null和Undefined

    Void通常用以表示函数或方法没有返回值,Never表明永远不会发生的类型。

    function log(name: string):void {
        console.log(name)
    }
    
    // 抛出了错误,程序永远没有返回值
    function error(message: string): never {
        throw new Error(message);
    }
    // 函数内有一个死循环,永远不会执行到循环外的代码
    function infiniteLoop(): never {
        while (true) {
        }
    }
    复制代码

    Null和Undefined类型为其它全部类型的子类型,这意味着该类型能够赋值为其它任何类型,但这必须在strictNullChecks编译选项为false的状况下。

    let n: null = null
    let u: undefined = undefined;
    let str: string = '1212'
    str = n
    复制代码

自定义类型

  1. 内联方式

    let person: {name: string, age: number} = {name: '小龙女', age: 12}
    复制代码

    内联方式声明自定义类型较为直接,但其声明的类型没法复用。

  2. 声明函数

    函数能够直接使用function声明类型,如函数变量则能够直接经过内联方式声明。

    // 使用function进行声明
    function log(str: string): void {
      console.log(str);
    }
    
    // 对函数变量进行声明
    interface Log {
      (str: string): boolean;
    }
    type Log2 = {
      (str: string): boolean;
    }
    const myLog: Log = str => {
      console.log(str);
      return true;
    };
    // 直接使用箭头进行函数声明
    const toDo: (str: string) => void = str => {
      console.log(str);
    };
    复制代码
  3. 经过class声明类型

    es6新增的class能够直接声明类型,并且对于静态属性、静态函数的声明很是方便:

    class Cat {
      name: string;
      static Type: string;
    }
    
    // Cat.Type 会是string类型
    
    const cat: Cat = new Cat();
    复制代码
  4. interface和type

      interface和type大同小异,均可用于自定义类型,建议直接使用interface声明类型,在interface没法实现时使用type。

    interface Point {
      x: number;
      y: number;
      // 加?号表示可选
      x2?: number;
      // 声明方法
      computed?():void
    }
    // 同名接口合并,下面接口能够与上面的Point接口合并
    interface Point {
      x2: number;
    }
    // 接口继承
    interface Point_3D extends Point {
      z: number;
    }
    // 接口能够被class实现
    class ChartPoint implements Point {
      x: number;
      y: number;
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }
    }
    // 使用type声明一个类型
    type Person = {
        name: string;
        age: number;
        sex: string;
        showInfo(isLog: boolean):string;
    }
    // 为Point接口取个别名
    type Point2 = Point
    复制代码

    有一些状况下只能使用type进行自定义类型声明:

    interface Person {
      name: string;
      age: number;
      sex: string;
      id: string;
    }
    // SubPerson类型的全部属性都存在于Person类型中
    type SubPerson = {
      [key in keyof Person]: Person[key];
    };
    
    function createSubPerson( person: Person, keys: Array<keyof Person> ): SubPerson {
      return keys.reduce(
        (obj, key) => ({ ...obj, [key]: person[key] }),
        {} as SubPerson
      );
    }
    
    复制代码

字面量类型

  我认为学习ts比较重要的是区分类型与变量,类型和变量是能够重名的,有些操做是针对类型,如keyof;有些则是针对变量, 如instanceof;有些则是二者皆可, 如typeof。事实上分清类型与变量一点也不容易。在ts中,值也是一种类型——字面量类型(literal type)

// 全部的值均可以做为类型
// 字面量类型
let foo: "Hello"; // foo的值只能为Hello
foo = "Hello";
let num: 12;
num = 12;
let bool: true = true;
let obj2: { a: 121 } = { a: 121 };
复制代码

变量只能为一个固定的值,这样看来字面量类型彷佛没有什么价值?结合联合类型会让其大有所为:

type Strs = "a" | "b" | "c" | "d";
type Nums = 1 | 2 | 3 | 4 | 5;
type Bools = true | false;
let str:Strs = 'a' // str只能为a、b、c、d中的一个

// 对象和联合类型实现枚举类型的功能
// 枚举类型是一种奇特的类型,既能够充当类型,又能够当成对象进而访问它的属性

// strEnum将数组转为对象
function strEnum(o: Array<T>): { [K in T]: K } {
  return o.reduce((res, key) => {
    res[key] = key;
    return res;
  }, Object.create({}));
}
const OTHER = strEnum(["North", "South", "East", "West"]); //此处OTHER是对象
type OTHER = keyof typeof OTHER; // 此处OTHER为类型

// 如今OTHER具有了枚举类型的所有特性
let other2: OTHER;
other2 = OTHER.East;
复制代码
相关文章
相关标签/搜索