一、Typescript.json 配置文件
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
: 数组类型,排除的库名
- 如不设定
files
和include,ts
默认是exclude
以外的所有的以.ts
和.tsx
结尾的文件。如果,同时设置files
的优先级最高,exclude
次之,include
最低。 - 上面都是文件相关的,编译相关的都是靠
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"
// ]
}
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 的类型进阶
- 联合类型与交叉类型很有关联,但是使用上却完全不同。联合类型表示一个值可以是几种类型之一。 我们用竖线
(|)
分隔每个类型,所以number | string | boolean
表示一个值可以是number
,string
,或boolean
。如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员。 - 联合类型适合于那些值可以为不同类型的情况,区分
2
个可能值的方法是检查成员是否存在。TypeScript
里的类型保护机制,就是一些表达式,它们会在运行时检查以确保在某个作用域里的类型。类型保护也可以解决联合类型中的潜在的类型问题,语法不规范的问题等等。对于类型保护,有以下的一些情况,如下所示:
- 通过类型断言的方式,以及自身逻辑的理解去确保解决代码的潜在问题报错的问题,判断参数是哪一个类型。
animal
是联合类型,既可以是Bird
,也可以是Dog
。而通过类型断言告诉它是Bird
,具有sing()
方法。 in
语法来做类型保护,如果sing
在animal
中,说明就是接口Bird
,就有这个sing()
方法,反之就是Dog
有bark()
方法。typeof
语法来做类型保护, 对于联合类型,通过typeof
判断参数的类型,是属于哪一种,进行类型保护,执行相应的逻辑处理。- 使用
instanceof
语法来做类型保护,instanceof
类型保护是通过构造函数来细化类型的一种方式。instanceof
的右侧要求是一个构造函数,TypeScript
将细化为,此构造函数的prototype
属性的类型,如果它的类型不为any
的话,构造签名所返回的类型的联合。只有类才有instanceof
, 接口没有instanceof
。当参数为联合类型的时候,使用instanceof
判断参数是否是属于那个类,执行相应的操作,进行类型保护。
- 对于联合类型和类型保护的代码,如下所示:
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;
}
}
- 枚举,使用枚举我们可以定义一些带名字的常量, 使用枚举可以清晰地表达意图或创建一组有区别的用例,
TypeScript
支持数字的和基于字符串的枚举。使用枚举,通过枚举的属性来访问枚举成员,和枚举的名字来访问枚举类型。枚举类型如下所示:
- 数字枚举,不带初始化器的枚举或者被放在第一的位置,或者被放在使用了数字常量或其它常量初始化了的枚举后面,数字枚举有自增长行为。
- 字符串枚举,在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化。由于字符串枚举没有自增长的行为,字符串枚举可以很好的序列化。字符串枚举允许你提供一个运行时有意义的并且可读的值,独立于枚举成员的名字。
- 异构枚举,枚举可以混合字符串和数字成员。
- 计算的和常量成员,每个枚举成员都带有一个值,它可以是 常量或计算出来的。枚举成员被当作是常量,情况如下
- 它是枚举的第一个成员且没有初始化器,这种情况下它被赋予值
0
。 - 它不带有初始化器且它之前的枚举成员是一个 数字常量。 这种情况下,当前枚举成员的值为它上一个枚举成员的值加
1
。 - 举成员使用 常量枚举表达式初始化, 常数枚举表达式是
TypeScript
表达式的子集,它可以在编译阶段求值。
- 它是枚举的第一个成员且没有初始化器,这种情况下它被赋予值
- 字面量枚举成员,一种特殊的非计算的常量枚举成员的子集,不带有初始值的常量枚举成员,或者是值被初始化为任何字符串字面量、任何数字字面量和应用了一元 -符号的数字字面量。当所有枚举成员都拥有字面量枚举值时,枚举成员成为了类型,枚举类型本身变成了每个枚举成员的 联合。
enum
枚举默认第一个为0
,后面依次递归加1
,那么输出值为0 1 2
。如果修改值,设置ONLINE
为4
,那么输出值为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 的泛型进阶
- 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能,同时也要考虑可重用性。使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据, 这样用户就可以以自己的数据类型来使用组件。泛型,可以适用于多个类型。泛型函数后,可以用两种方法使用。 第一种是,传入所有的参数,包含类型参数。第二种,利用了类型推论,即编译器会根据传入的参数自动地帮助我们确定T的类型。
- 泛型,也可以理解为泛指的类型。在
join
中接收T
这个类型,T
就是一个泛型,可以是任意类型,String、Number
等等都可以,指定first
和second
就是这个T
泛型。当在使用join
的时候,指定T
这个泛型为number
类型, 那么里面的所有参数必须也是number
类型,否则就会报错。除了参数可以用泛型T
,在返回结果中也同样可以用T
这个泛型。在map
中接收T
这个泛型,params
就是数组,里面接收T
这个泛型,Array<T>
与T[]
是等价的。当在使用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");
- 泛型类看上去与泛型接口差不多。 泛型类使用( <>)括起泛型类型,跟在类名后面。类有两部分:静态部分和实例部分,泛型类指的是实例部分的类型,所以类的静态属性不能使用这个泛型类型。泛型约束,只要传入的类型有这个属性,我们就允许,就是说至少包含这一属性,需要有约束条件。定义一个接口来描述约束条件,使用这个接口和
extends
关键字来实现约束。我们需要传入符合约束类型的值,必须包含必须的属性。在泛型里使用类类型,TypeScript
使用泛型创建工厂函数时,需要引用构造函数的类类型,使用原型属性推断并约束构造函数与类实例的关系。 - 在类中使用泛型,
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;
版权声明:本文为weixin_42614080原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。