Typescript 的语法进阶基础之Typescript.json 配置文件、类型进阶和泛型进阶

一、Typescript.json 配置文件
  1. tsconfig.json 所包含的属性并不多,只有 7 个,官方也给出了它的定义文件,如下所示:
  • files: 数组类型,用于表示由 ts 管理的文件的具体文件路径
  • exclude: 数组类型,用于表示 ts 排除的文件(2.0 以上支持 Glob
  • include: 数组类型,用于表示 ts 管理的文件(2.0 以上)
  • compileOnSave: 布尔类型,用于 IDE 保存时是否生成编译后的文件
  • extends: 字符串类型,用于继承 ts 配置,2.1 版本后支持
  • compilerOptions: 对象类型,设置编译的选项,不设置则使用默认配置,配置项比较多,后面再列
  • typeAcquisition: 对象类型,设置自动引入库类型定义文件(.d.ts)相关,该对象下面有 3 个子属性分别是:
    • enable: 布尔类型,是否开启自动引入库类型定义文件(.d.ts),默认为 false
    • include 数组类型,允许自动引入的库名,如:["jquery", "lodash"]
    • exculde: 数组类型,排除的库名
  1. 如不设定 filesinclude,ts 默认是 exclude 以外的所有的以 .ts.tsx 结尾的文件。如果,同时设置 files 的优先级最高,exclude 次之,include 最低。
  2. 上面都是文件相关的,编译相关的都是靠 compilerOptions 设置的,代码如下所示:
{
  "compilerOptions": {
    // "incremental": true,                       // 增置编译
    // "tsBuildlnfoFile": "./buildFile",          // 增置编译文件的存储位置  // "diagnostics": true,              // 打印诊断倍息
    // "target": "es5",                           // 目标语言的版本
    // "module": "commonjs",                      // 生成代码的模块标准
    // "outFile": "./app-js",                     // 将多个相互依赖的文件生成一个文件,可以用在AMD模块中
    // "lib": [],                                 // TS 需要引用的库,即声明文件,es5 默认"dom", "es5", "scripthost"
    // "allowJs": true,                              // 允许编译:IS 文件(js、jsx)
    // "checkJs": true,                           // 允许在JS文件中报错,通常与allowJS _起使用
    // "outDir": "./dist",                        // 指定输出目录
    // "rootDir": "./",                           // 指定输入文件目录(用于输出)
    // "declaration": true,                       // 生成声明文件
    // "declarationDir": "./d",                   // 声明文件的路径
    // "emitDeclarationOnly": true,               //    只生成声明文件
    // "sourceMap": true,                         // 生成目标文件的    sourceMap
    // "inlineSourceMap": true,                   // 生成目标文件的 inline sourceMap
    // "declarationMap": true,                    // 生成声明文件的 sourceMap
    //  "typeRoots":[],                           // 声明文件目录,默认node_modules/@types
    //  "types":[],                               // 声明文件包
    //  "removeComments":true,                    // 删除注释
    //    "noEmit": true,                           // 不输出文件
    //    "noEmitOnError": true,                    // 发生错误时不输出文件
    // "noEmitHelpers": true,                     // 不生成helper 函数,需要额外安装ts-helpers
    // "importHelpers": true,                     // 通过tslib 引入 helper 函数,文件必须是模块
    // unoEmitOnErrorM: true,                     // 发生错误时不输出文件
    // "noEmitHelpers": true,                     // 不生成 helper 函数,需额外安装 ts-helpers 
    // "importHelpers": true,                     // 通过tslib引入helper函数,文件必须是模块
    // "downlevellteration" : true,               // 降级遍历器的实现(es3/5)
    // "strict": true,                            // 开启所有严格的类型检查
    // "alwaysStrict": false,                     // 在代码中注入"use strict";
    // "noImplicitAny": false,                    // 不允许隐式的any类型
    // "strictNullChecksilj false,                // 不允许把null、undefined赋值给其他类型变置
    // "strictFunctionTypes": false,              // 不允许函数参数双向协变
    // "strictPropertyInitialization": false,     // 类的实例属性必须初始化
    // strictBindCallApply: false,                // 严格的 bind/call/apply 检査
    // "noImplicitThis": false,                   // 不允许this有隐式的any类型
    // "noUnusedLocals": true,                    // 检査只声明,未使用的局部变置 
    // "nollnusedParameters": true,               // 检查未使用的函数参数
    // "noFallthroughCasesInSwitch": true,        // 防止switch语句贯穿
    // "noImplicitReturns": true,                 // 每个分支都要有返回值
    // "esModulelnterop": true,                   // 允许export = 导出,由import from导入 
    // "allowUrndGlobalAccess": true,             // 允许在模块中访问UMD全局变置
    // "moduleResolution": "node",                // 模块解析策略
    // "baseUrl": "./",                           // 解析非相对模块的基地址
    // "paths": {
    //    "jquery": ["node-modules/jquery/dist/jquery.slim.min.js"]
    //  }
    // "rootDirs": ["src", "out"],                // 将多个目录放在一个虚拟目录下,用于运行时
    // "listEmittedFiles": true,                  // 打印输出的文件
    // "listFiles": true,                         // 打印编译的文件(包括引用的声明文件)
  },
  // "include": [                                 // 指定一个匹配列表(属于自动指定该路径下的所有ts相关文件)
  //   "src/**/*.ts",
  //   "src/**/*.tsx",
  //   "src/**/*.vue",
  //   "tests/**/*.ts",
  //   "tests/**/*.tsx"
  // ],
  // "exclude": [                                 // 指定一个排除列表(include的反向操作)
  //   "node_modules"
  // ],
  // "files": [                                   // 指定哪些文件使用该配置(属于手动一个个指定文件)
  //   "demo.ts"
  // ]
}
  1. tsconfig.app.json,代码如下所示:
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "./out-tsc/app",
    "types": [] // https://stackoverflow.com/questions/39826848/typescript-2-0-types-field-in-tsconfig-json
  },

  // 需要编译的文件
  "files": [
    "src/main.ts",
    "src/polyfills.ts"
  ],

  // 需要编译的文件
  // files 和 include 两个属性的组合定义了所有要编译的文件
  "include": [
    "src/**/*.ts"
  ],

  // 不需要编译的
  "exclude": [
    "src/test.ts",
    "src/**/*.spec.ts"
  ]
}
二、Typescript 的类型进阶
  1. 联合类型与交叉类型很有关联,但是使用上却完全不同。联合类型表示一个值可以是几种类型之一。 我们用竖线(|)分隔每个类型,所以 number | string | boolean 表示一个值可以是 numberstring,或 boolean。如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员。
  2. 联合类型适合于那些值可以为不同类型的情况,区分 2 个可能值的方法是检查成员是否存在。TypeScript里的类型保护机制,就是一些表达式,它们会在运行时检查以确保在某个作用域里的类型。类型保护也可以解决联合类型中的潜在的类型问题,语法不规范的问题等等。对于类型保护,有以下的一些情况,如下所示:
  • 通过类型断言的方式,以及自身逻辑的理解去确保解决代码的潜在问题报错的问题,判断参数是哪一个类型。animal 是联合类型,既可以是 Bird,也可以是 Dog。而通过类型断言告诉它是 Bird,具有 sing() 方法。
  • in 语法来做类型保护,如果 singanimal 中,说明就是接口 Bird,就有这个 sing() 方法,反之就是 Dogbark() 方法。
  • typeof 语法来做类型保护, 对于联合类型,通过 typeof 判断参数的类型,是属于哪一种,进行类型保护,执行相应的逻辑处理。
  • 使用 instanceof 语法来做类型保护,instanceof 类型保护是通过构造函数来细化类型的一种方式。instanceof 的右侧要求是一个构造函数,TypeScript 将细化为,此构造函数的 prototype 属性的类型,如果它的类型不为 any 的话,构造签名所返回的类型的联合。只有类才有 instanceof , 接口没有 instanceof 。当参数为联合类型的时候,使用 instanceof 判断参数是否是属于那个类,执行相应的操作,进行类型保护。
  1. 对于联合类型和类型保护的代码,如下所示:
interface Bird {
  fly: boolean;
  sing: () => {};
}

interface Dog {
  fly: boolean;
  bark: () => {};
}

function trainAnial(animal: Bird | Dog) {
  if (animal.fly) {
    (animal as Bird).sing();
  } else {
    (animal as Dog).bark();
  }
}

function trainAnialSecond(animal: Bird | Dog) {
  if ("sing" in animal) {
    animal.sing();
  } else {
    animal.bark();
  }
}

function add(first: string | number, second: string | number) {
  if (typeof first === "string" || typeof second === "string") {
    return `${first}${second}`;
  }
  return first + second;
}

class NumberObj {
  count: number;
}

function addSecond(first: Object | NumberObj, second: Object | NumberObj) {
  if (first instanceof NumberObj && second instanceof NumberObj) {
    return first.count + second.count;
  } else {
    return 0;
  }
}

  1. 枚举,使用枚举我们可以定义一些带名字的常量, 使用枚举可以清晰地表达意图或创建一组有区别的用例, TypeScript 支持数字的和基于字符串的枚举。使用枚举,通过枚举的属性来访问枚举成员,和枚举的名字来访问枚举类型。枚举类型如下所示:
  • 数字枚举,不带初始化器的枚举或者被放在第一的位置,或者被放在使用了数字常量或其它常量初始化了的枚举后面,数字枚举有自增长行为。
  • 字符串枚举,在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化。由于字符串枚举没有自增长的行为,字符串枚举可以很好的序列化。字符串枚举允许你提供一个运行时有意义的并且可读的值,独立于枚举成员的名字。
  • 异构枚举,枚举可以混合字符串和数字成员。
  • 计算的和常量成员,每个枚举成员都带有一个值,它可以是 常量或计算出来的。枚举成员被当作是常量,情况如下
    • 它是枚举的第一个成员且没有初始化器,这种情况下它被赋予值 0
    • 它不带有初始化器且它之前的枚举成员是一个 数字常量。 这种情况下,当前枚举成员的值为它上一个枚举成员的值加 1
    • 举成员使用 常量枚举表达式初始化, 常数枚举表达式是 TypeScript 表达式的子集,它可以在编译阶段求值。
  • 字面量枚举成员,一种特殊的非计算的常量枚举成员的子集,不带有初始值的常量枚举成员,或者是值被初始化为任何字符串字面量、任何数字字面量和应用了一元 -符号的数字字面量。当所有枚举成员都拥有字面量枚举值时,枚举成员成为了类型,枚举类型本身变成了每个枚举成员的 联合。
  1. enum 枚举默认第一个为 0,后面依次递归加 1,那么输出值为 0 1 2。如果修改值,设置 ONLINE4,那么输出值为 0 4 5。除了创建一个以属性名做为对象成员的对象之外,数字枚举成员还具有了 反向映射,从枚举值到枚举名字,代码如下所示:
enum Status {
  OFFLINE,
  ONLINE,
  DELETED,
}

function getResult(status) {
  if (status === Status.OFFLINE) {
    return "offline";
  } else if (status === Status.ONLINE) {
    return "online";
  } else if (status === Status.DELETED) {
    return "deleted";
  }
  return "error";
}

console.log(getResult(Status.OFFLINE));

三、Typescript 的泛型进阶
  1. 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能,同时也要考虑可重用性。使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据, 这样用户就可以以自己的数据类型来使用组件。泛型,可以适用于多个类型。泛型函数后,可以用两种方法使用。 第一种是,传入所有的参数,包含类型参数。第二种,利用了类型推论,即编译器会根据传入的参数自动地帮助我们确定T的类型。
  2. 泛型,也可以理解为泛指的类型。在 join 中接收 T 这个类型,T 就是一个泛型,可以是任意类型,String、Number 等等都可以,指定 firstsecond 就是这个 T泛型。当在使用 join 的时候,指定 T 这个泛型为 number 类型, 那么里面的所有参数必须也是 number 类型,否则就会报错。除了参数可以用泛型 T,在返回结果中也同样可以用 T 这个泛型。在 map 中接收 T 这个泛型,params 就是数组,里面接收 T 这个泛型,ArrayT[] 是等价的。当在使用 map 的时候,接收泛型为 string 类型,里面的数组也必须是 string 类型。泛型中除了可以写单个 泛型,还可以写多个泛型,为参数指定不同的类型。如果没写类型匹配,TS 会进行类型推断,代码如下所示:
function join<T>(first: T, second: T) {
  return `${first}${second}`;
}

join<number>(1, 1);

function authjoin<T>(first: T, second: T): T {
  return first;
}

authjoin(1, 2);

function map<T>(params: Array<T>) {
  return params;
}

map<String>(["123"]);

function add<T, P>(first: T, second: P) {
  return `${first}${second}`;
}

add<number, string>(1, "2");

  1. 泛型类看上去与泛型接口差不多。 泛型类使用( <>)括起泛型类型,跟在类名后面。类有两部分:静态部分和实例部分,泛型类指的是实例部分的类型,所以类的静态属性不能使用这个泛型类型。泛型约束,只要传入的类型有这个属性,我们就允许,就是说至少包含这一属性,需要有约束条件。定义一个接口来描述约束条件,使用这个接口和 extends 关键字来实现约束。我们需要传入符合约束类型的值,必须包含必须的属性。在泛型里使用类类型,TypeScript 使用泛型创建工厂函数时,需要引用构造函数的类类型,使用原型属性推断并约束构造函数与类实例的关系。
  2. 在类中使用泛型,T 继承接口 Item,用于 Item 的属性 name。constructor 构造器约束 data 是私有属性,是数组,值为里面 T 泛型,约束了 name 必须为 string 类型。在使用类的时候,创建对象,使用泛型。在类中泛型,也可以使用联合泛型。使用泛型,也可以作为一个具体的类型注解,代码如下所示:
interface Item {
  name: string;
}

class DataManager<T extends Item> {
  constructor(private data: T[]) {}
  getItem(index: number): string {
    return this.data[index].name;
  }
}

const data = new DataManager([{ name: "Tom" }]);

class DataManager2<T extends number | String> {
  constructor(private data: T[]) {}
  getItem(index: number): T {
    return this.data[index];
  }
}

function hello<T>(params: T) {
  return params;
}

const func: <T>(param: T) => T = hello;

你可能感兴趣的:(Javascript,typescript,Typescript.json,类型进阶,泛型进阶)