## 静态属性
类的静态成员存在于类本身上面而不是类的实例上,在这个例子里,我们使用static定义origin,因为它是所有网格都会用到的属性,如同在实例属性上使用this,前缀来访问属性一样,这里我们使用Grid.来访问静态属性
``` class Grid { static origin = {x: 0, y: 0}; getOrigin(){ return Grid.origin.x; } } ```
## 抽象类
抽象类作为其他派生类的基类使用。它们一般不会直接被实例化。不同于接口,抽象类可以包含成员的实现细节。abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法。抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。
``` abstract class Animal { abstract makeSound(): void; // 若继承必须实现makeSound方法 move(): void { console.log('roaming the earch...'); } } ```
# 函数
## 函数类型
typescript支持给每个参数添加类型之后再为函数本身添加返回值类型
``` function add(x: number, y: number): number { return x + y; } ```
## 书写完整函数类型
函数的类型由参数类型和返回值组成,书写完整的函数类型以保证API信息的完整
## 可选参数
typescript中传递给一个函数的参数个数必须与函数期望的参数个数一致,在javascript中,每个参数都是可选的,可传可不传,在typescrip中要实现类似的功能,可以在参数名旁使用?实现可选参数的功能
``` function buildName(firstName: string, lastName?: string) { if(lastName) { return firstName + " " + lastName; }else { return firstName; } } ```
可选参数必须跟在必须参数后面,如果上例想让first name可选的,那么就必须调整它们的位置,把first name放在后面。
## 默认参数
typescript可以为参数提供一个默认值
``` function buildName(firstName: string, lastName = "Smith") { return firstName + "" + lastName; } ```
## 剩余参数
如果想同时操作多个参数,或者你并不知道会有多少参数传递进来,可以在...后面给定名字来定义这样剩余参数
``` function buildName(firstName: string, ... restOfName: string[]) { return firstName + " " + restOfName.join(""); } let employeeName = buildName("joseph", "samuel", "lucas", "mackinzie"); ```
## 重载
为了让编译器能够选择正确的检查类型,它与javascript里的处理流程相似。它查找重载列表,尝试使用第一个重载定义。如果匹配的话就使用这个。因此,在定义重载的时候,一定要把最精确的定义放在最前面。
``` function pick(x; {suit: string; card: number;}[]): number; funciton pick(x: number): {suit: string; card: number;}; function pick(x): any {} ```
# 泛型
对于大型系统时,需要考虑组件API的可重用性,组件不仅要能够支持当前数据类型,同时还需要支持未来的数据类型,所以引入泛型的概念来创建可重用的组件
可能有些同学会觉得可以使用any类型,但是这样会导致类型的不确定性,你给到一个number类型的参数希望结果也是number类型,any类型并不能实现这个效果
** 泛型的最大特点在于通过变量来表示类型,而不是具体的值 **
## 泛型接口
``` function identity<arg: T>: T{ return arg; } ```
## 泛型类
``` class GenericNumber<T> { zeroValue: T; add: {x: T, y: T} => T; } ```
## 泛型约束
当需要操作某个类型的某个属性,比如数组的length,但是因为用的是泛型,并不能确定这个属性是否有length,这里可以定义一个接口来描述约束条件,创建一个包含.length属性的接口,使用这个接口和extends关键字来实现约束
``` interface Lengthwise{ length: number; } function loggingIdentity<T extends Lengthwise>(arg: T): T{ console.log(arg.length); return arg; } ```
## 在泛型约束中使用类型参数
可以声明一个类型参数,且它被另一个类型参数所约束,比如我们想要用属性名从对象里获取这个属性,且要确保这个属性存在于对象obj上
``` function getProperty(obj: T, key: K){ return obj[key]; } ```
## 在泛型里使用类类型
在Typescript使用泛型创建工厂函数时,需要引用构造函数的类类型
``` function create<T>(c: {new(): T;}): T { return new c(); } ```
# 枚举
使用枚举可以定义一些带名字的常量,可以清晰地表达意图或者创建一组有区别的用例,typescript支持数字和基于字符串的枚举
## 数字枚举
通过枚举的属性来访问枚举成员,和枚举的名字来访问枚举类型
``` enum Response { No = 0, Yes = 1 } function respond(recipient: string, message: Response): void { // ... } respond("Princess Caroline", Response.Yes); ```
## 字符串枚举
``` enum Direction { Up = "Up", Down = "Down", Left = "Left", Right = "Right" } ```
## 混合枚举
从技术的角度来说,枚举可以混合字符串和数字成员,但是不建议这么做
## 计算和常量成员
每个枚举成员都带有一个值,它可以是常量或计算出来,当满足如下条件时,枚举成员被当作是常量:
- 它是枚举的第一个成员且没有初始化器,这种情况下它被赋予值0
``` enum E{x} ```
- 它不带有初始化器且它之前的枚举成员是一个数字常量。这种情况下,当前枚举成员的值为它上一个枚举成员的值加1
``` enum E{ A = 1,B,C } ```
- 枚举成员使用常量枚举表达式初始化。常数枚举表达式是typescript表达式的子集,它可以在编译阶段求值。
# 类型兼容性
与js中不同的,typescript的object的类型会区分各种各样的类,并不能像js中随意赋值
## 基本规则
如果x要兼容要,那么y至少具有与x相同的属性,即y是否能赋值给x,编译器检查x中的每个属性,看是否能在y中也找到对应属性
# 高级类型
## 交叉类型
把多种类型叠加到一起成为一种类型,下面是如何创建混入的一个简单例子:
``` function extend<T, U>(first: T, second: U): T&U { let result = <T & U>{}; for(let id in first) { (<any>result)[id] = (<any>first)[id]; } for(let id in second) { if(!result.hasOwnProperty(id)) { (<any>result)[id] = (<any>second)[id]; } } return result; } ```
# 模块
typescript与ES5一样,任何包含顶级import或者export的文件都被当作一个模块。相反地,如果一个文件不带顶级的import或者export声明,那么它的内容被视为全局可见的
# 命名空间
我们需要一种手段来组织代码,以便于在记录它们类型的同时还不用担心与其它对象产生命名冲突。因此,我们把验证器包裹到一个命名空间内,而不是把它们放在全局命名空间下
## 分离到多文件
当应用变得越来越大时,我们需要将代码分离到不同的文件中以便于维护
``` // Validation.js namespace Validation { export interface StringValidator { isAcceptable(s: string): boolean; } } ```
## 别名
另一种简化命名空间操作的方法是使用`import q = x.y.z;`给常用的对象起一个短的名字
``` namespace Shapes { export namespace Polygons { export class Triangle{}; export class Square{}; } } import polygons = Shapes.Polygons; let sg = new polygons.Square(); ```
# 声明合并
"声明合并"是指编译器将针对同一个名字的两个独立声明合并为单一声明。合并后的声明同时拥有原先两个声明的特性,任何数量的声明都可被合并;不局限两个声明
## 合并接口
最简单也是最常见的合并类型是接口合并。从根本上说,合并的机制是把双方的成员放到一个同名的接口里。
``` interface Box { height: number; width: number; } interface Box { scale: number; } ```
这两个接口将合并成一个声明:
``` interface Box { height: number; width: number; scale: number; } ```
## 合并命名空间
同接口
## 非法合并
typescript并非允许所有的合并。目前,类不能与其它类或者变量合并