ts的核心原则之一是关注值的 形状(shape),接口interface就充当着命名这些类型的角色。
Our first interface
function printLabel(params: {label: string}) {
console.log(params.label)
}
let myParams = {label: 'interface', usage: 'type'};
printLabel(myParams);
注意我们的参数myParams里不止有label属性,但类型检查器(type checker)只会检查需要的属性是否有被提供。若已提供,视为类型匹配成功。
不要大意,ts的类型检查一般不会这么宽松,这里只是特例。
interface labelParams {
label: string;
}
function printLabel(params: labelParams) {
console.log(params.label)
}
let myParams = {label: 'interface', usage: 'type'};
printLabel(myParams);
不同于其他语言,我们不用显式的说明我们的传参实现了接口(interface),只需要传递的参数满足所需即可(ts匹配需要的属性以及它们的类型)。
Optional Properties
一个接口中并不是所有属性都是必须的,在传递给一个函数一个对象参数时,可能只有给部分属性赋值了。
interface square {
width?: number;
color?: string;
}
function createSquare(obj: square) {
let newSquare = { color: 'red', area: 100 };
if (obj.width) {
newSquare.area = obj.width * obj.width;
}
if (obj.color) {
newSquare.color = obj.color;
}
return newSquare;
}
createSquare({color: 'blue'});
可选属性optional properties 的好处在于,我们既描述了这些可能存在的属性,同时又拦截了interface定义范围以外的属性。
比如当错误输入某些属性名时,ts会告警报错:
interface square {
width?: number;
color?: string;
}
function createSquare(obj: square) {
let newSquare = { color: 'red', area: 100 };
if (obj.widt) { // Error: Property 'widt' does not exist on type 'square'. Did you mean 'width'?
newSquare.area = obj.width * obj.width;
}
if (obj.color) {
newSquare.color = obj.color;
}
return newSquare;
}
Readonly properties
属性名之前有readonly关键字的属性,只能在首次赋值时被修改。
ts中有个ReadonlyArray<T>的泛型 = 不可修改版的Array<T>。你甚至不能把这个数组赋值给别的变量,当然,你可以使用断言assertion来向编译器证明谁才是老大。
let aArray:Array<number> = [1,2,3,4];
let anotherArr: ReadonlyArray<number> = aArray;
anotherArr[0] = 1; // Index signature in type 'readonly number[]' only permits reading
let arr1: number[] = anotherArr; // The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'
let arr2: number[] = anotherArr as number[]
readonly vs const
判断是使用readonly还是const,取决于你想将只读赋予属性还是变量。
Excess Property Checks
前面的例子里,我们成功地将类型{label: 'interface', usage: 'type'}的值传递给了{ label: string;};同时,我们也学习了optional properties。
然而,组合上述两种用法却有可能导致错误:
interface labelParams {
label?: string;
}
function printLabel(params: labelParams) {
console.log(params.label)
}
printLabel({label: 'interface', usage: 'type'}); // Argument of type '{ label: string; usage: string; }' is not assignable to parameter of type 'labelParams'.
// Object literal may only specify known properties, and 'usage' does not exist in type 'labelParams'
可能你会觉得多传一个usage属性并没有什么大不了,但是ts认为对象字面量传递给变量或者作为参数时应该接受更严格的类型检查。
绕过这类检查其实很简单:
- 最简单的方法:类型断言
printLabel({label: 'interface', usage: 'type'} as labelParams);
- 更好的方法:字符串索引签名
string index signature
如果你确定接口可以接收更多的属性,可以这样写:
interface labelParams {
label?: string;
[propName: string]: any;
}
- 令人惊讶的办法:将
object literals赋值给变量
let labelOption = {label: 'interface', usage: 'type'};
printLabel(labelOption);
注意:也许你不应该想尽办法去绕过
excess type checks,因为那很有可能就是一个bug。