类型断言
可以用来手动指定一个值的类型。
有两种方式:
- <类型>值
- 值 as 类型(在tsx语法中必须用这一种)
例子:
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
经常用在将一个联合类型的变量指定为一个更加具体的类型。有时候,我们确实需要在还不确定类型的时候就访问一个类型的属性或方法。
类型断言不是类型转换,断言成一个联合类型中不存在的类型是不允许的。
交叉类型
将多个类型合并为一个类型。这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。例如: Firest & Second 同时是 First 和 Second 。就是说这个类型的对象同时拥有了这两种类型的成员。
联合类型
表示一个值可以是几种类型之一。我们用竖线(|
)分隔每个类型,所以 number | string | boolean 表示一个值可以是 number , string , boolean。
如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员。
类型守卫
类型守卫就是一些表达式,它们会在运行时检查以确保在某个作用域里的类型(也就是一旦检查过类型,就能在之后的每个分支里清楚地知道这个类型)。
有下面几种方法:
typeof
typeof 类型守卫只有两种形式能被是被: typeof v === 'typename'
和 typeof v !== 'typename'
,typename
必须是 number
, string
, boolean
, symbol
。
instanceof
instanceof 类型守卫是通过构造函数来细化类型的一种方式。 instanceof 的右侧要求是一个构造函数。
class Person {
name: "xiaomuzhu";
age = 20;
}
class Animal {
name = "petty";
color = "pink";
}
function getSomething(arg: Person | Animal) {
if (arg instanceof Animal) {
console.log(arg.color);
console.log(arg.age); // Error
}
if (arg instanceof Person) {
console.log(arg.age);
console.log(arg.color); // Error
}
}
in
in 操作符可以作为类型细化表达式来使用。
function move(pet: Fish | Bird) {
if ("swim" in pet) {
return pet.swim();
}
return pet.fly();
}
字面量类型守卫
e Foo = {
kind: "foo";
foo: number;
};
type Bar = {
kind: "bar";
bar: number;
};
function doStuff(arg: Foo | Bar) {
if (arg.kind === "foo") {
console.log(arg.foo);
console.log(arg.bar); // error
} else {
console.log(arg.foo); // error
console.log(arg.bar);
}
}
声明合并
如果定义了两个相同名字的函数、接口或类,那么它们会合并成一个类型。
函数的合并:可以使用重载定义多个函数类型
接口的合并:接口中的属性在合并时会简单的合并到一个接口中。(注意:合并的属性的类型必须是唯一的)接口中方法的合并,与函数的合并一样。
类的合并:类的合并与接口的合并规则一致。
注释的妙用
可以通过 /** */
来注释 TypeScript 的类型,当我们在使用相关类型的时候就会有注释的提示,这个技巧在多人协作开发的时候十分有用,我们绝大部分情况下不用去花时间翻文档或者跳页去看注释。
装饰器
装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为。
通俗的讲装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能。
目前装饰器本质上是一个函数, @expression
的形式其实是一个语法糖, expression
求值后必须也是一个函数,它会在运行时被调用,被装饰的声明信息作为参数传入。
// 普通装饰器
function logClass(params: any) {
console.log(params);
// params 就是当前类
params.prototype.apiUrl = 'xxx';
}
// 注意这里不用写分号
@logClass
class HttpClient {
constructor() {}
getData() {}
}
装饰器工厂
如果我们要定制一个修饰器如何应用到一个声明上,我们得写一个装饰器工厂函数。装饰器工厂就是一个简单的函数,它返回一个表达式,以供装饰器在运行时调用。
function color(value: string) { // 这是一个装饰器工厂
return function(target: any) { // 这是装饰器
// ...
}
}
类装饰器
类装饰器在类声明之前被声明(紧接着类声明)。类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。其参数是类的构造函数。
用装饰器重载类
function logClass(target: any) {
return class extends target {
apiUrl: any = '修改后'
getData() {}
}
}
@logClass
class HttpClient {
public apiUrl: string | undefined;
constructor() {
this.apiUrl = 'aa';
}
getData() {
console.log(this.apiUrl);
}
}
var http = new HttpClient();
http.getData();
属性装饰器
属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 成员的名字
方法装饰器
方法装饰器会被应用到方法的属性描述符上,可以用来监视,修改或者替换方法定义。方法装饰器会在运行时传入下列3个参数:
- 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 成员的名字(要修改的方法名)
- 成员的属性描述符
方法参数装饰器
参数装饰器表达式会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一个新的属性,属性中包含一系列信息,这些新奇就被称为元数据
,然后我们就可以使用另外一个装饰器来读取元数据
。传入下列3个参数:
- target:对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 方法名称
- index:参数在函数参数列表中的索引
各种装饰器的执行顺序
- 属性装饰器
- 方法参数装饰器
- 方法装饰器
- 类装饰器
- 如果有两个相同类型的装饰器,先执行写在后面的那一个装饰器。
模块和命名空间
模块
模块是自声明的,两个模块之间的关系是通过在文件级别上 import 和 export 建立的。简单来说,只要你在文件中使用了 import 和 export 语法,就可以将其视为一个模块。 typescript 官方文档如是说: typescript 和 es6 一样,任何包含顶级 import 或者 export 的文件都被当成一个模块。其实,也不一定是顶级。
namespace
在代码量较大的情况下,为了避免各种变量命名相冲突,可将相似功能的函数、类、接口等放置到命名空间内。如果想在命名空间外访问,则用 export 导出。用关键字 namespace 来声明命名空间。
命名空间的引用
通常情况下,声明的命名空间代码和调用的代码不在同一个文件里。
//biology.ts
namespace Biology {
export interface Animal {
name: string;
eat(): void;
}
export class Dog implements Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
eat() {
console.log(`${this.name} 吃狗粮。`};
}
}
// app.ts
/// <reference path="biology.ts" />
let dog: Biology.Animal;
dog = new Biology.Dog('狗狗');
dog.eat();
通过 reference
注释引用命名空间,即可通过“完全限定名”进行访问。我们也可以通过 import
导入模块的形式,引入命名空间。
import '.biology'
let dog: Biology.Animal;
dog = new Biology.Dog('狗狗');
dog.eat();
别名
简化命名空间的操作方法:import q = x.y.z
给常用的对象起一个短的名字。但不要和加载模块 import x = require("name")
的语法弄混了,这里的语法只是为指定的符号创建一个别名而已。
例子:
namespace Shapes {
export namespace Polygons {
export class Triangle {}
export class Square {}
}
}
import polygons = Shapes.Polygons;
let sq = new polygons.Square();