在javascript中,组织数据以及传递数据通常都会使用object来完成,在typescript中,object类型有着更加丰富的定义,发挥着更加明显的作用,这篇博客记录一些个人知识的薄弱点
一、如何描述一个object
描述一个对象,可以通过type关键字声明一个类型别名,或者使用interface来定义一个接口。
1.可选属性描述符?
当在操作一个对象时,可能存在部分属性是可选的情况。在typescript中,这种可选属性可以通过操作符?来定义,如下:
type Example = {
name?:string
}
需要注意的是,可选代表可能不存在,因此name属性的类型是undefined | string的联合类型。
2.可选属性搭配解构赋值
在使用可选属性时,不可避免会遇到空值处理的情况。假如存在以下场景:
interface PaintOptions {
xPos?: number;
yPos?: number;
}
function paintShape(opts: PaintOptions) {
let xPos = opts.xPos === undefined ? 0 : opts.xPos;
let yPos = opts.yPos === undefined ? 0 : opts.yPos;
}
上述代码paintShape函数就是针对空值进行处理,并赋予一个初始值。这种模式虽然增强了代码的健壮性,但当代码中存在很多类似的判断时,不可避免会降低代码的可读性。在这种情况下,就可以使用结构解构**的语法来替代这种冗长的赋值代码。
interface PaintOptions {
xPos?: number;
yPos?: number;
}
function paintShape({xPos = 0,yPos = 0}: PaintOptions) {
}
通过这种方式,极其方便的减少了代码量。
二、索引标签
1.基础定义
在很多时候,我们并不知道一个对象的所有属性,但却知道一个对象的属性结构,在这种情况下,便可以使用索引标签来定义。定义方式如下:
interface StringArray {
[index: number]: string;
}
此时只要属性名是字符串,属性值也是字符串,便满足结构。需要注意的是,属性名的类型,只能是string | number。
2.扩展定义
虽然使用索引标签可以约束对象的部分结构类型,但是也还可以进行扩展定义,比如把已知的属性定义出来。
interface NumberDictionary {
[index: string]: number;
length: number; // ok
name: string; // error
}
这个定义方式表明对象不仅可以有不确定的属性,还有2个确定的属性。但这个地方会产生一个错误,那便是name属性的名称是string,但值是string,不满足满足了索引标签的约束,即属性名是string,则属性值必须是number。此时也可以进行值得扩展,声明为一个联合类型来解决。
interface NumberDictionary {
[index: string]: number | string;
length: number; // ok
name: string; // ok
}
当然这种为了兼容而不断扩大类型范围,本身就会导致定义的约束变宽,这种方式的适用性,还得根据具体的情况来斟酌使用。
三、只读类型的区别
1.只读属性
当声明对象类型时,可以使用修饰符readonly来定义只读属性,但在校验2个类型是否兼容时,并不会考虑只读属性,比如:
type A = {
readonly a:number
}
type B = {
a:number
}
let b:B = {
a:10
}
let a:A = b
上述示例中,虽然A类型和B类型声明上存在区别,但在赋值操作时,类型校验忽略了readonly关键字。
2.只读数组
除了常规使用的数组类型外,还存在一个类型叫只读数组。如下:
let arr:ReadonlyArray<string> = [] // 只读数组
let arr1:Array<string> = []
arr1 = arr // error
arr = arr1 // ok
只读数组不能赋值给一个普通数组,但普通数组可以赋值给一个只读数组。
这个地方其实很好理解,数组的赋值操作,本质上是改变了引用的指针。如果只读数组允许赋值给普通数组,那么普通数组可以修改数组,那么此时便会修改到只读数组,所以不允许赋值。反之,只读数组无法修改数组的值,因此不会有影响。
3.只读元组
元组类型是另一种数组类型,简单理解为结构固定的数组类型,可以有定长和不定长2种定义方式。如下:
type StringNumberPair = [string, number]; // 固定长度
type StringNumberBooleans = [string, number, ...boolean[]]; // 不定长度
type StringBooleansNumber = [string, ...boolean[], number];
type BooleansStringNumber = [...boolean[], string, number];
const a: StringNumberBooleans = ["hello", 1];
const b: StringNumberBooleans = ["beautiful", 2, true];
const c: StringNumberBooleans = ["world", 3, true, false, true, false, true];
const d: StringNumberPair = ['hello',2]
当没有使用省略符时,元组就是长度和类型都确定的数组,当使用了省略符时,元组类型就是不定长的,但固定位置的元素类型被确定。
元组通用可以使用readonly来声明,只读元组不允许修改元素内容,这个本质就是和数组一致
type StringNumberPair = readonly [string, number];
let a:StringNumberPair = ['hello',1]
a[1] = 2 // error