上一篇(TypeScript常见的数据类型):http://t.csdn.cn/UjGrn
。
TS中除了上篇文档中描述的常见数据类型之外,还有很多高级数据类型,本文档列举一些常用的类型。
TS全面支持ES6规范中的class类定义,并且对其进行了增强,如添加了类型注解、实现接口、可见性修饰符等等。
/**
* TS中的class,不仅提供了class的语法功能,定义的类也作为一种类型存在
*/
class Person{
name: string;
age: number;
constructor(name: string,age: number){
this.name = name;
this.age = age;
}
}
const p: Person = new Person("张三",18);
实现接口:ES规范中的class类可以继承父类,TS对其进行了增强,使其可以实现接口。
/**
* class类通过implements关键字实现接口
* 子类必须显示提供父接口中所有属性
* 子类可以同时实现多个接口,使用 , 分隔
*/
interface Person{
name: string,
say: Function
}
interface Behave{
eat(): void
}
class Student implements Person,Behave{
name = '姓名';
say = ()=>{
console.log('say hello');
}
eat = ()=>{
console.log('吃饭');
}
}
可见性修饰符:TS对class类 属性/方法 添加了可见性修饰符,用来控制class的方法或属性对于class外的代码是否可见。
可见性修饰符包括:1 public(公共的,默认) 2 protected(受保护的) 3 private(私有的)。
public修饰符
/**
* public:表示该属性为公开的,在任何地方均可访问(默认修饰符,可省略)
* 当前类内部、子类、当前类外部均可访问
*/
class Person{
public name: string = '名字'; //同 name: string; 写法作用一致
getName = ()=>{
return this.name; //本类中可访问
}
}
let p = new Person();
console.log(p.name);//Person类外部也可访问
protected修饰符
/**
* protected:表示受保护的,仅对其声明所在类和子类中(非实例对象)可见。
*/
class Person{
protected name: string = '名字';
protected getName = ()=>{
return this.name; //本类中可访问
}
}
class Student extends Person{
printName = ()=>{
console.log(this.name); //子类中可访问
}
}
let s = new Student();
s.printName();
let p = new Person();
//console.log(p.name); //Person类外部不可访问,编译报错
private修饰符
/**
* private:表示私有的,只在当前类中可见,对实例对象以及子类也是不可见的。
*/
class Person{
private name: string = '名字';
private getName = ()=>{
return this.name; //本类中可访问
}
}
class Student extends Person{
printName = ()=>{
//console.log(this.name); //编译报错,子类无法访问
}
}
let s = new Student();
//s.name; //Person类外部不可访问,编译报错
readonly修饰符:表示属性只读,用来防止在构造函数之外对属性进行赋值
class Person{
readonly name: string = '名字';
constructor(name: string){
this.name = name; //构造函数可以为readonly属性赋初值
}
setName = (name: string){
//this.name = name; //编译报错:Cannot assign to 'name' because it is a read-only
}
}
两种类型系统:1:Structural Type System(结构化类型系统),2:Nominal Type System(标明类型系统)。
TS采用的是结构化类型系统,类型检查关注的是值所具有的结构,如果两个对象具有相同的结构,则认为它们属于同一类型。
标明类型系统,两个对象的类型若要相等,就必须具有相同“名字”的类型(Java、C#)。
class类兼容
class Position{x: number=0;y: number=0}
class Position2{x: number=0;y: number=0}
class Position3{x: number=0;y: number=0;z: number=0}
//Position、Position2结构相同,所以类型相同
const p: Position = new Position2();
/**
* Position3包含Position2所有的结构(Position2⊆Position3)
* 所以Position2可以作为Position3的对象的类型,反之则不成立
*/
const p2: Position2 = new Position3();
接口兼容
interface Position{x: number;y: number}
interface Position2{x: number;y: number}
interface Position3{x: number;y: number;z: number}
/**
* 接口兼容性与class类兼容性类似
*/
let p: Position = {x:0,y:0};
let p2: Position2 = p;
let p3: Position3 = {x:0,y:0,z:0};
p2 = p3;
函数兼容
函数兼容性涉及到:1、参数个数,2、参数类型,3、返回值类型。
待续。。。
交叉类型(&):功能类似于接口继承(extends),用于组合多个类型为一个类型(常用于对象类型)。
interface A{a: number};
interface B{b: number};
type AB = A&B;
//使用类型交叉后,类型AB就具有了类型A、B的属性结构
let ab: AB = {
a:1,
b:2
}
交叉类型(&)和接口继承(extends)的对比:
相同点:都可以实现对象类型的组合。
不同点:两种方式实现类型组合时,对于同名属性之间,处理类型冲突的方式不同。
interface A{a: number};
interface B{a: string,b: number};
type AB = B&A;
/**
* 当A、B接口中存在同名属性时,可以使用类型交叉
* 注意:当同名属性不同类型时,可能会被合并为 never 类型(不能被满足的类型)
*/
// let ab: AB = {
// a:1,//此时属性a为never类型(不能满足a即为number、又为string),编译报错
// b:2
// }
// interface C extends A{
// a: string //直接编译报错,类型冲突
// }
泛型是可以在保证类型安全(不丢失类型信息)前提下,让函数等与多种类型一起工作,从而实现复用,常用于:函数、接口、class中。
泛型函数
/**
* 创建泛型函数
* 语法:在函数名称的后面添加 <>(尖括号),尖括号中添加类型变量,比如此处的Type(符合命名规范即可)。
* 类型变量 Type,是一种特殊类型的变量,它处理类型而不是值。
* 因为 Type 是类型,因此可以将其作为函数参数和返回值的类型,表示参数和返回值具有相同的类型。
*/
function fun<Type>(param: Type): Type{
return param;
}
let numResult = fun<number>(10);
let strResult = fun<string>('10');
/**
* 调用泛型函数时,可以省略 <类型> 来简化泛型函数的调用
* 充分利用TS的类型参数推断机制,TS根据实际参数类型反推Type类型
* 说明:当编译器无法推断类型或者推断的类型不准确时,就需要显式地传入类型参数
*/
let booleanResult = fun(true);
/**
* 为泛型添加约束
* 通过extends关键字,确保传入的参数类型继承过IAnimal接口,或拥有name属性(参考类型兼容性)
* 为什么使用泛型约束:类型变量可以代表任意类型,同时也导致实际传入的对象无法访问任何属性,有时就需要为泛型添加约束来收缩类型(缩窄类型取值范围)。
*/
interface IAnimal{name: string}
function eat<T extends IAnimal>(animal: T):void{
console.log(animal.name);//如果不添加IAnimal类型约束,此处访问name属性就会报错:Property 'name' does not exist on type 'T'
}
/**
* 泛型变量可以多个
*/
function estimate<T,R>(par1: T,par2: R):void{}
泛型接口
/**
* 在接口名称的后面添加 <>(尖括号),那么这个接口就变成了泛型接口。
* 接口的类型变量,对接口中所有其他成员可见,也就是接口中所有成员都可以使用类型变量。
*/
interface GenInter<S,N> {
name: S,
age: N,
introduce: (name:S,age:N)=>void
}
/**
* 使用泛型接口时,需要显式指定具体的类型。
*/
const person: GenInter<string,number> = {
name:'张三',
age:18,
introduce: (name,age)=>{
console.log(`my name is ${name} and I am ${age} years old`);
}
}
泛型class
/**
* 在类名称的后面添加 <>(尖括号),那么这个类就变成了泛型类。
*/
class Person<T,K>{
name: T;
age: K;
constructor(name: T,age: K){
this.name = name;
this.age = age;
}
}
/**
* 在创建 class 实例时,在类名后面通过 <类型> 来指定明确的类型。
*/
const p = new Person<string,number>("张三",18);
console.log(p.name,p.age);
泛型工具类:TS内置了很多基于泛型实现的常用工具类,来简化TS中的一些常见操作。如Partial
、Readonly
、Pick
、Record
等。