一、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" : {
} ,
}
tsconfig.app.json
,代码如下所示:
{
"extends" : "./tsconfig.json" ,
"compilerOptions" : {
"outDir" : "./out-tsc/app" ,
"types" : [ ]
} ,
"files" : [
"src/main.ts" ,
"src/polyfills.ts"
] ,
"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;