CONTENT

Typescript中接口不仅能够作用于类中,还能为属性、函数、可索引的类型提供约束和规范。它的核心原则之一就是为值所具有的结构进行类型检查。

  接口初探

下面通过一个简单示例来观察接口是如何工作的:


function sayHello(person: {name: string}): void {
    console.log("Hello,My name is " + person.name);
}
let pJane = {sex: "girl",name: "Jane",age: 13};
sayHello(pJane);

类型检查器会检查sayHello的调用,这里会要求该方法的参数满足Person类型的约束。 需要注意的是,我们传入的对象参数实际上会包含很多属性,但是编译器只会检查那些必需的属性是否存在,并且其类型是否匹配。 然而,有些时候TypeScript却并不会这么宽松:

下面我们重写上面的例子,这次使用接口来描述:必须包含一个name属性且类型为string

interface Person{
    name: string;
}
function sayHello(person: Person): void {
    console.log("Hello,My name is " + person.name);
}
let pJane = {sex: "girl",name: "Jane",age: 13};
sayHello(pJane);

Person接口就好比一个人,用来描述上面例子里的要求。 它代表了有一个 name属性且类型为string的对象。 需要注意的是,我们在这里并不能像在其它语言里一样,说传给 sayHello的对象实现了这个接口。我们只会去关注值的外形。 只要传入的对象满足上面提到的必要条件,那么它就是被允许的。

还有一点值得提的是,类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型也是对的就可以。

可选属性

接口里的属性不全都是必需的。 有些是只在某些条件下存在,或者根本不存在。 可选属性在应用“option bags”模式时很常用,即给函数传入的参数对象中只有部分属性赋值了。

下面是应用了“option bags”的例子:

interface CardConfig{
    type?: string;
    width?: number;
}

function createCard(cardConfig: CardConfig): {type: string,size: number}{
    let newCard = {type: "queen",size: 100};
    if(cardConfig.type) {
        newCard.type = cardConfig.type;
    }
    if(cardConfig.width) {
        newCard.size = cardConfig.width * cardConfig.width;
    }
    return newCard;
}

let myCard = createCard({type:"king"});

带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个?符号。

可选属性的好处之一是可以对可能存在的属性进行预定义,好处之二是可以捕获引用了不存在的属性时的错误。 比如,我们故意将 createCard里的type属性名拼错,就会得到一个错误提示:

interface CardConfig{
    type?: string;
    width?: number;
}

function createCard(cardConfig: CardConfig): {type: string,size: number}{
    let newCard = {type: "queen",size: 100};
    if(cardConfig.type) {
        //Property 'typ' does not exist on type 'CardConfig'. Did you mean 'type'?ts(2551)
        newCard.type = cardConfig.typ;
    }
    if(cardConfig.width) {
        newCard.size = cardConfig.width * cardConfig.width;
    }
    return newCard;
}

let myCard = createCard({type:"king"});

  可读属性

一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly来指定只读属性:

interface Point{
    readonly x: number;
    readonly y: number;
}

你可以通过赋值一个对象字面量来构造一个Point。 赋值后, xy再也不能被改变了。

let p1: Point = { x: 10, y: 20 };
//Error Cannot assign to 'x' because it is a read-only property.ts(2540)
p1.x = 5; 

TypeScript具有ReadonlyArray<T>类型,它与Array<T>相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改:

let nums: number[] = [1, 2, 3, 4, 5];
let iNums: ReadonlyArray<number> = nums;
//Index signature in type 'readonly number[]' only permits reading.ts(2542)
iNums[0] = 10;
//Property 'push' does not exist on type 'readonly number[]'.ts(2339)
iNums.push(12);
//Cannot assign to 'length' because it is a read-only property.ts(2540)
iNums.length = 14;
//The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.ts(4104)
nums = iNums;

上面代码的最后一行,可以看到就算把整个ReadonlyArray赋值到一个普通数组也是不可以的。 但是你可以用类型断言重写:

nums = iNums as number[];

readonly vs const

最简单判断该用readonly还是const的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用 const,若做为属性则使用readonly

Comments | NOTHING

暂无评论...