一、基本类型
ts支持跟js一样所有的基本类型,此外还增加了枚举类型使用。
布尔值、number(二进制、十进制、八进制等)、字符串、数组(指定数组类型 let arr:number[]=[1,2,3];元组 let arr:[number,string] = [10,‘haha’])、Object、
枚举:enum类型是对js基本类型的一个补充,使用枚举可以为一组数值提供赋予友好的名字
eg:
enum:Color = {Red,Green,Blue}
let c:Color = Color.Green;
//默认情况下,从0开始为元素编号,也可以为名字指定值
enum Color = {Red = 2,Green};
let colorName:string = Color[2];//Red
Any:
任何类型,在指定的变量类型不确定或返回的类型不确定时使用,可以让编译器不对类型进行检查,可以是任何类型。
Void:
void类型跟any类型正好相反,它表示没有任何类型。当一个函数没有返回值时,通常指定返回值类型是void。
Null和undfined
null和undifined跟js一样,也具有这两个基本类型,且此两个类型是任何其他基本类型的子类型,可以赋值给任何类型。
let a:number = null
let b:void = null
never:
nerver类型代表着永远没有返回值,通常给异常函数进行使用或者根本没有返回值的函数使用,never类型是所有基本类型的子类型,其他类型都可以是never值,但是never值不可以是其他任何类型的值。
类型断言:
类型断言就是告诉编译器不用进行编译时校验,采取相信程序员的类型标记。
//类型断言有两种声明方式
1、尖括号语法
let someString:any = 'hahaha';
let strLength:number = (<string>someString).length;
2、as语法
let someString:any = 'hahaha';
let strLength:number = (someString as string).length;//jsx时只能使用此语法
二、变量声明
ts变量声明跟ES6 js变量声明一致,也是const及let、类型解构;
//对象解构
let {a:newName1,b:newName2} = o
// o={
a:1,
b:2
}
//也可指定类型
let {a,b}:{a:number,b:string} = o
三、接口
1、接口就是ts里用于指定组合类型的,里面可以组合各种类型,然后组合为接口。指定一个变量为这个接口类型。
interface LabelledValue{
label:string
}
function printLabel(labelObject:LabelledValue){
console.log(labelObject.label)
}
let myObj = {size:10,label:'hahaha'}
printLabel(myObj)
接口内只要传入的类型有接口里面具有的类型,就匹配成功,接口内还有多个其他类型属性也不影响。还可以专门指定接口内的属性为可选属性。
2、可选属性:可选属性跟普通属性差不多,只是属性名后多了一个?。
interface LabelledValue{
label:string,
val?:number
}
function printLabel(labelObject:LabelledValue){
console.log(labelObject.label)
}
let myObj = {size:10,label:'hahaha'}
printLabel(myObj)
3、只读属性:在变量名前指定readonly,即表明此属性,只能在创建时被赋值一次,以后再也不能被更改了。
interface LabelledValue{
readonly label:string,
val?:number
}
//ts具有ReadonlyArray<T>类型,它与Array<T>相似,只是不可变,也可以确保数组创建后不能被修改
let a:number[] = [1,2,3,4];
let ro:ReadonlyArray<number> = a;
rop[0] = 12;//error
4、函数类型:可以定义函数类型的接口,定义函数的入参类型,返回类型
interface SearchFunc{
(source:string,subString:string) : boolean;
}
let mySearch:SearchFunc;
mySearch = function(src:string,sub:string):boolean{//入参名可以与定义的接口不一致,但是各参数的类型需一致
let result = src.search(sub);
return result > -1;
}
5、可索引的类型:可索引类型具有一个索引签名,它描述了对象索引的类型,以及相应索引的返回值。
interface StringArray{
[index:number]:string
}
let myArray:StringArray;
myArray = ['a','b'];
let myStr:string = myArray[0];
6、类类型:与java等类似,也可以使用类类型来明确强制一个类的类型
interface ClockInterface{
currentTime:Date;
setTime(d:Date);
}
class Clock implements ClockInterface{
currentTime: Date;
setTime(d: Date) {
this.currentTime = d;
}
constructor(h:number,m:number){
}
}
7、继承接口:接口与类一样,也可以进行继承。
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
8、混合接口;接口继承类
四、函数
ts中的函数跟js中的函数几乎一致,并且提供了一些拓展;
1、函数类型
function add(x: number, y: number): number {
return x + y;
}
let myAdd = function(x: number, y: number): number { return x + y; };
//完整函数类型
参数类型与返回类型组成完整函数类型,只要参数类型一致,参数名并不进行校验;左边ts会自动进行推断,因此完整的函数类型如下;
let myAdd:(baseValue:number,increment:number) => number = function(x: number, y: number): number { return x + y; };
2、可选参数
js里面的参数是可选的,如果没有进行赋值,会是undefined;但ts里面指明的参数都是必须的,但是可以指定可选参数,可选参数跟js的参数一样,可以不传。
function buildName(firstName:string,last?:string):string{
}
//可选参数必须在必须参数后面;
//ts中也可指定参数默认值,参数默认值指定方法跟js一致,默认参数可以在必须参数之前,但是传值时,如果不传默认的值,必须传undefinded;
3、剩余参数
跟在js中传入可拓展参数一样,剩余参数可以不传或传任意个
function buildName(firstName:string,...lastName:string[]):string{
}
4、重载
ts的函数还可以进行重载,那么ts的接口就有多态,重载两种状态。ts的重载会根据传入的参数类型,自动选择合适的返回类型状态。
function pickCard(x:{suit:string;card:number}[]):number;
function pickCard(x:number):{suit:string;card:number};
function pickCard(x):any{
}
//pickCard有两种重载状态,根据传入的参数是number还是Object进行选择;在描述重载状态时,应把最确定的放最上面,这样ts编译时能快速找到;
五、类
1、es6开始提供了类,ts中也支持类,并且更加友好,更接近面向对象的用法。
class Greeter{
greetingf:string;
constructor(message:string){
this.greeting = message;
}
greet(){
return "Hello," + this.greeting;
}
}
let greet = new Greeter("world");
2、继承
ts原生就支持类,并且支持面向对象用法,因此也支持继承模式
class Animal {//基类 / 超类
name:string
constructor(name:string){
this.name = name;
}
move(distanceInMeters: number = 0) {
console.log(`Animal moved ${distanceInMeters}m.`);
}
}
class Dog extends Animal {//派生类 / 子类
bark() {
console.log('Woof! Woof!');
}
}
class Snak extends Animal{
constructor(name:string){//派生类 / 子类里的构造函数必须调用super,代表调用基类的构造函数。ts规定。
super(name)
}
}
3、公有、私有与受保护类型
ts跟其他面向对象的编程一样,类中所有的属性,构造函数,函数都可以指定类型;
公有类型:public 关键字默认不写,不写即默认为public,本类及子类及本子类的实例都能够访问;
私有类型:private 需要属性名前书写,私有类型只能在本类内进行访问;
class Animal {//基类 / 超类
private name:string
constructor(name:string){
this.name = name;
}
move(distanceInMeters: number = 0) {
console.log(`Animal moved ${distanceInMeters}m.`);
}
}
受保护类型:protected 需要属性名前书写,受保护类型只能在本类及派生类内进行访问,本类实例及派生类实例也不能进行访问;
class Animal {//基类 / 超类
protected name:string
constructor(name:string){
this.name = name;
}
}
//readonly 只读属性,只可读访问,不可修改
class Animal {//基类 / 超类
readonly name:string
constructor(name:string){
this.name = name;
}
}
4、存取器-即get set操作
ts允许在类中定义存取器,即get set操作
let passcode = 'secret passcode'
class Employee{
private _fullName:string;
get fullName():string{
return this._fullName;
}
set fullName(newName:string):string{
if(passcode == 'secret passcode'){
this._fullName = newName;
}else{
consolo.log("Error")
}
}
}
5、静态属性
一般情况类内指定的属性都为实例初始化时才执行的属性,严格意义上属于实例的属性,也可以通过指定静态属性,此属性为类的属性。通过 类名.静态属性名 才能在类中进行访问
class Grid {
static origin = {x:0,y:0};
calOrigin(point:{x:number;y:number}){
let x = (point.x - Grid.origin.x)
}
constructor (public scale:number)
}
6、抽象类
ts中可以定义抽象类作为基类,抽象类中也可以定义抽象方法,定义的抽象方法只能在子类中实现,抽象类不能实例化。而基于抽象类的子类实例也不能调用抽象类中没有的方法。
抽象类及抽象方法使用 abstract 关键字定义
abstract class Department {
constructor(public name :string){
}
abstract printMeeting():void //必须在派生类中实现
}
class AccDep extends Department {
constructor(){//基类必须调用
super('acc');
}
printMeeting():void{
console.log(123);
}
report():void{//此方法即使定义了,AccDep的实例也不能进行调用
console.log(456);
}
}
六、泛型
1、ts类似与java存在泛型来设置一些重复的组件,譬如函数。ts中的泛型类似于any,可以指代任何类型的值,但跟any不同的是,泛型只是指代传入的类型,而any是不确定类型。泛型使用类型变量T表示,而此类型变量只是指定类型,不表示值。
function identity<T>(arg:T):T{//泛型函数
return arg
}
//使用
(1)指定传入类型
let i = identity<string>("haha")
(2)类型推论--即编译器自动判别
let i = identity("haha")
//可以用于表示T数组 T[]
function identity<T>(arg:T[]):T[]{//泛型函数
return arg
}
2、泛型接口,泛型接口思想跟泛型函数类似
interface Gen{
<T>(arg:T):T;
}
function identiy<T>(arg:T):T{
}
ler i:Gen = i;//调用i时参数确认类型
interface Gen<T>{
(arg:T):T;
}
function identiy<T>(arg:T):T{
}
ler i:Gen<number> = i;//整个接口泛型T指定统一类型
3、泛型类,泛型类思想跟泛型函数及泛型接口类似
class Gen<T>{//只有实例部分可以使用泛型,静态部分不能,因为静态部分不是由外部操控,只能类内操作,属于类内。
value:T
}
let i = new Gen<number>()//整个类指定泛型类型,更加直观
4、泛型约束
有时候,并不希望所有的类型都能符合要求,这个时候就可以对泛型进行约束,只有符合条件的才行。
//最常见的是使用接口进行约束
interface lengthwise{
length:number;
}
function log<T extends lengthwise>(arg:T):T{
}
七、枚举
枚举是一组变量名的集合,可以为其指定值。
1、数字枚举,指定值或开始值,如果开始值是number,则自动开始递增,如果不指定,则第一个自动从0开始。
enum Direction {
one=1,
two,//2
three//3
}
//若不指定one=1,则one=0,two=2,three=3
//访问数据枚举
Direction.two = 2
2、字符串枚举
字符串枚举不会进行自递增
enum Direction {
one='first',
two='second',//second
three='third'//third
}
//若不指定one=1,则one=0,two=2,three=3
//访问数据枚举
Direction.two = second
极少情况下才将数字枚举与字符串枚举进行混合。
3、枚举成员按值的求法分为常量成员和计算成员
值为常量的或常量引用的等为常量成员;
表达式等的为计算成员;
数字枚举可以从枚举值反向映射枚举值;
常量枚举,常量枚举对象成员只能是常量枚举表达式:
const enum Directions {
Up,
Down,
Left,
Right
}
let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right]
var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];
//且编译阶段就会被删除,内联到引用的代码处
八、类型兼容性
ts的组合类型为结构性类型,跟java的名义类型不一样,不要求两个结构完全一摸一样才能进行赋值或传参。只要x要兼容y,那么y中至少具有与x中相同的属性,可多,不可少。
interface Named{
name:string;
}
let x : string;
let y = {name:'a',location:'l'}
x=y//通过编译
//函数传参也同理;
九、高级类型
1、交叉类型:交叉类型代表多个类型的联合体,使用&,表示一个对象或此处类型为联合类型。
例如, Person & Serializable & Loggable同时是 Person 和 Serializable 和 Loggable。 就是说这个类型的对象同时拥有了这三种类型的成员。
2、联合类型:表示一个值可以是几种类型之一。 我们用竖线( |)分隔每个类型,所以 number | string | boolean表示一个值可以是 number, string,或 boolean。
3、用户自定义的类型保护,ts里类型保护就是一些表达式,运行时检查每个作用域里的类型。
定义一个检查函数,返回值是一个类型谓词
function isFish(pet:Fish | Bird):pet is Fish{
return (<Fish>pet).swim !== undefined;
}
十、模块
ts中的模块与es6中的模块思想是一致的。模块内的内容只有模块内可用,export出去的话外部可用。若没有export,也没有import,则为全局内容。
//commonJs与AMD里的exports变量,即等于es6内的export default;ts使用export = 表示;使用 import x = require(‘xxx.ts’)
class test{
}
export = test
import t = require('test.ts')
可以在导出文件内,再次导出第三方模块;
import {a} from 'a.ts';
....
export {a} from 'a.ts'
十一、命名空间
1、ts中的命名空间,即同一文件内,指定一个内部作用域空间。此空间内变量都是此内部作用域的,如需访问需抛出内部变量/类/方法;使用 空间名.作用名 。
namespace Validation {
export interface stringValidation {//外部需要使用需要export导出
isAcceptable(s:string):boolean
}
const lttersRegexp = /^[A-Za-z]+$/;//外部不需要使用,外部也无法访问
export class LettersOnlyValidator implements stringValidation {
isAcceptable(s: string) {
return lttersRegexp.test(s);
}
}
}
let strings = ["Hello", "98052", "101"];
let validator : {[s:string]:Validation.stringValidation} = {};//外部访问要使用 空间名.导出变量 访问
validator["letter"] = new Validation.LettersOnlyValidator();
2、同一命名空间分布于多文件内
同一命名空间内的代码可以分段分布于多文件内,引用时使用引用即可
//one.ts
namespace Validation {
export interface stringValidation {//外部需要使用需要export导出
isAcceptable(s:string):boolean
}
}
//two.ts
namespace Validation {
const lttersRegexp = /^[A-Za-z]+$/;//外部不需要使用,外部也无法访问
export class LettersOnlyValidator implements stringValidation {
isAcceptable(s: string) {
return lttersRegexp.test(s);
}
}
}
//test.ts
<reference path="one.ts">
<reference path="two.ts">
let strings = ["Hello", "98052", "101"];
let validator : {[s:string]:Validation.stringValidation} = {};//外部访问要使用 空间名.导出变量 访问
validator["letter"] = new Validation.LettersOnlyValidator();
十二、声明合并
ts会把同名的命名空间、接口等进行合并,形成一个并集。
1、合并接口
同名的接口声明会自动进行合并,如果两个同名声明存在同样的非函数成员,则这两个成员必须类型一致。
接口的合并,是后来合并的接口内成员靠前。
2、合并命名空间
命名空间的合并与接口类似,但是只合并导出的成员,未导出的成员,仍然只能在原始的命名空间内进行访问。
3、同名的命名空间还能与同名的类、接口、枚举等合并,但合并的内部同名成员类型需要相同。
十三、装饰器
1、装饰器可以对类或其他方法添加额外的特性支持,或修改类及其成员。@xxx为装饰器声明,通常声明在标注的元素(类)前。xxx为一个函数,参数为被装饰的声明信息。
@sealed
Class s{
}
function sealed(target){//target即为Class s
}
//实际上通常创建一个装饰器工厂函数,返回一个表达式,以供装饰器运行时调用
function color(v:string){//这是一个装饰器工厂
return function (target){//这是一个装饰器
}
}
2、多个装饰器;当具有多个装饰器修饰同一个声明时,由上而下依次对装饰器表达式求值(即返回装饰器函数);运行时,求值的结果(即装饰器函数)由下而上调用;
3、类装饰器
类装饰器在类声明的前面,类装饰器的参数是类的构造函数
@sealed
class Greeter{
greeting:string;
constructor(msg:string){
this.greeting = msg;
}
}
function sealed(constructor:Function){
Object.seal(constructor);
Object.seal(constructor.prototype)
}
4、方法装饰器
方法装饰器思想跟类装饰器一样。但装饰器表达式入参有三个。
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
@enumerable(false)
greet() {
return "Hello, " + this.greeting;
}
}
function enumerable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {//第一个参数是构造函数,第二个参数是成员名字,第三个参数是成员的属性描述符
descriptor.enumerable = value;
};
}
5、属性装饰器
class Greeter {
@format("Hello,%s")
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
十四、Mixins 及三斜线指令
1、ts中的mixins是一种思想,即把一个组件的代码重复组合使用。可以是引入,也可以是遍历对象或类,复制成员到目标对象。
2、三斜线指令是指在单文件包含xml标签中的最顶端的几个注释,单文件中最开头的即是注释也是指令
///<reference path="...">
十五、js文件类型检查
ts是js的超集,ts文件内书写js语法,也是允许的,且编译时会通过一定的检查及编译。