前言
TypeScript 的核心原则之一是对值所具有的结构进行类型检查
-
认识接口
function fn(e:{target:{value:string}}){ }
使用接口重写
interface parm { target:{ value:string } } function fn(e:parm){ }
-
可选属性
接口里的属性不全是必须的,即给函数传入的参数对象中只有部分属性赋值了。
interface parm { target:{ value?:string, name:string } } function fn(e:parm){ } fn({target:{name:'我的名字'}})
-
只读属性
一些对象属性只能在对象刚创建的时候修改其值,可以使用readonly来指定只读属性。
interface User{ readonly name:string, readonly age:number } let user:User={name:'张三',age:24} user.name='李四' //error!
TypeScript具有ReadonlyArray类型,它与Array相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改:
let a:number[]=[1,2,3,4,5,6,7]; let result:ReadonlyArray<number>=a; result[0]=24;//error! let b=result;//error! //可以使用类型断言重写进行赋值 b=result as number[];
-
额外的属性检查
观察下面的例子
interface User{ name?:string, age?:number } function fn(user:User){}; let testFn=fn({myName:'我的名字',age:35}); //绕过检查方法 testFn=fn({myName:'我的名字',age:35}as User);
按照之前的可选属性,age属性符合要求,myname属性不存在可忽略,没有错误,可Typescript会提示myName错误。 对象字面量会被特殊对待而且会经过 额外属性检查,当将它们赋值给变量或作为参数传递的时候。 如果一个对象字面量存在任何“目标类型”不包含的属性时,你会得到一个错误。
-
函数类型
为了使用接口表示函数类型,我们需要给接口定义一个调用签名。 它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。
interface FnFunc{ (name:string,age:number):string } let fn:FnFunc=function(name:string,age:number){ return name+age; }
函数的参数会逐个进行检查,要求对应位置上的参数类型是兼容的,名字不用相同。
interface FnFunc{ (name:string,age:number):string } let fn:FnFunc=function(myName:string,myAge:number){ return myName+myAge; }
-
可索引的类型
TypeScript支持两种索引签名:字符串和数字。
可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。 这是因为当使用 number来索引时,JavaScript会将它转换成string然后再去索引对象。 也就是说用 100(一个number)去索引等同于使用"100"(一个string)去索引,因此两者需要保持一致。class Animal { name: string; } class Dog extends Animal { breed: string; } // 错误:使用数值型的字符串索引,有时会得到完全不同的Animal! interface NotOkay { [x: number]: Animal; [x: string]: Dog; }
字符串索引签名能够很好的描述dictionary模式,并且它们也会确保所有属性与其返回值类型相匹配。 因为字符串索引声明了 obj.property和obj[“property”]两种形式都可以。 下面的例子里, name的类型与字符串索引类型不匹配,所以类型检查器给出一个错误提示
interface NumberDictionary { [index: string]: number;//字符串索引返回number length: number; // 可以,length是number类型 name: string // 错误,`name`的类型与索引类型返回值的类型不匹配 }
-
类类型
interface ClockInterface { currentTime: Date; setTime(d: Date); } class Clock implements ClockInterface { currentTime: Date; setTime(d: Date) { this.currentTime = d; } constructor(h: number, m: number) { } }
类是具有两个类型的:静态部分的类型和实例的类型。这里因为当一个类实现了一个接口时,只对其实例部分进行类型检查
-
继承接口
和类一样,接口也可以相互继承。一个接口可以继承多个接口,创建出多个接口的合成接口。
interface Shape { color: string; } interface PenStroke { penWidth: number; } interface Square extends Shape, PenStroke { sideLength: number; } let square = <Square>{}; square.color = "blue"; square.sideLength = 10; square.penWidth = 5.0;
-
混合类型
接口能够描述JavaScript里丰富的类型,一个对象可以同时做为函数和对象使用,并带有额外的属性。
interface Counter { (start: number): string; interval: number; reset(): void; } function getCounter(): Counter { let counter = <Counter>function (start: number) { }; counter.interval = 123; counter.reset = function () { }; return counter; } let c = getCounter(); c(10); c.reset(); c.interval = 5.0;
-
接口继承类
当接口继承了一个类类型时,它会继承类的成员但不包括其实现。 就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样。 接口同样会继承到类的private和protected成员。 这意味着当你创建了一个接口继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现(implement)。
当你有一个庞大的继承结构时这很有用,但要指出的是你的代码只在子类拥有特定属性时起作用。 这个子类除了继承至基类外与基类没有任何关系。 例:
class Control { private state: any; } interface SelectableControl extends Control { select(): void; } class Button extends Control implements SelectableControl { select() { } } class TextBox extends Control { select() { } } // 错误:“Image”类型缺少“state”属性。 class Image implements SelectableControl { select() { } }