对象类型字面量是定义对象类型的方法之一。下例中,我们使用对象类型字面量定义了一个对象类型。该对象类型中包含了两个属性成员x
和y
,它们的类型均为number类型。示例如下:
const point: { x: number; y: number } = { x: 0, y: 0 };
// ~~~~~~~~~~~~~~~~~~~~~~~~
// 对象类型字面量
基础语法
对象类型字面量的语法与对象字面量的语法相似。在定义对象类型字面量时,需要将类型成员依次列出。对象类型字面量的语法如下所示:
{
TypeMember;
TypeMember;
...
}
在该语法中,TypeMember表示对象类型字面量中的类型成员,类型成员置于一对大括号“{}”
之内。
在各个类型成员之间,不但可以使用分号“;”
进行分隔,还可以使用逗号“,”
进行分隔,这两种分隔符不存在功能上的差异。
示例如下:
{
TypeMember,
TypeMember,
...
}
类型成员列表中的尾后分号和尾后逗号是可选的。示例如下:
{
TypeMember;
TypeMember;
}
{
TypeMember;
TypeMember
}
对象类型字面量的类型成员可分为以下五类:
- 属性签名
- 调用签名
- 构造签名
- 方法签名
- 索引签名
属性签名
属性签名声明了对象类型中属性成员的名称和类型。它的语法如下所示:
{
PropertyName: Type;
}
在该语法中,PropertyName
表示对象属性名,可以为标识符、字符串、数字和可计算属性名;Type
表示该属性的类型。
下例中,我们使用对象类型字面量定义了Point对象类型,该类型表示二维坐标系中的点。Point对象类型包含两个属性签名类型成员,分别为表示横坐标的属性x
和表示纵坐标的属性y
,两者的类型均为number类型。示例如下:
let point: { x: number; y: number } = { x: 0, y: 0 };
属性签名中的属性名可以为可计算属性名,但需要该可计算属性名满足以下条件之一:
- 可计算属性名的类型为string字面量类型或number字面量类型。示例如下:
const a: 'a' = 'a';
const b: 0 = 0;
let obj: {
[a]: boolean;
[b]: boolean;
['c']: boolean;
[1]: boolean;
};
- 可计算属性名的类型为
“unique symbol”
类型。示例如下:
const s: unique symbol = Symbol();
let obj: {
[s]: boolean;
};
- 可计算属性名符合
“Symbol.xxx”
的形式。示例如下:
let obj: {
[Symbol.toStringTag]: string;
};
在属性签名的语法中,表示类型的Type部分是可以省略的,允许只列出属性名而不定义任何类型。在这种情况下,该属性的类型默认为any
类型。示例如下:
{
x;
y;
}
// 等同于:
{
x: any;
y: any;
}
注意,此例中的代码仅在没有启用“--noImplicitAny”
编译选项的情况下才能够正常编译。若启用了“--noImplicitAny”
编译选项,则会产生编译错误,因为对象属性隐式地获得了any类型。示例如下:
{
x;
// ~
// 编译错误!成员 'x' 隐式地获得了 'any' 类型
}
在程序中,不推荐省略属性签名中的类型。
可选属性
在默认情况下,通过属性签名定义的对象属性是必选属性。如果在属性签名中的属性名之后添加一个问号“?”
,那么将定义一个可选属性。定义可选属性成员的语法如下所示:
{
PropertyName?: Type;
}
在给对象类型赋值时,可选属性可以被忽略。下例中,我们修改了前面定义的Point对象类型,添加一个可选属性z
来表示点的Z轴坐标。这样Point对象类型也能够表示三维坐标系中的点。示例如下:
let point: { x: number; y: number; z?: number };
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Point对象类型
point = { x: 0, y: 0 };
point = { x: 0, y: 0, z: 0 };
此例中,Point对象类型的属性z
是可选属性。在给point变量赋值时,既可以为属性z
赋予一个number类型的值,也可以完全忽略属性z
。
在“--strictNullChecks”
模式下,TypeScript会自动在可选属性的类型定义中添加unde-fined
类型。因此,下例中两个Point对象类型的定义是等价的:
{
x: number;
y: number;
z?: number;
};
// 等同于:
{
x: number;
y: number;
z?: number | undefined;
};
该行为的结果是,我们可以为可选属性传入undefined值来明确地表示忽略该属性的值,示例如下:
let point: { x: number; y: number; z?: number };
point = { x: 0, y: 0 };
point = { x: 0, y: 0, z: undefined };
point = { x: 0, y: 0, z: 0 };
同时也要注意,在“--strictNullChecks”
模式下,null类型与undefined类型是区别对待的。此例中,不允许给属性z
赋予null值,如下所示:
let point: { x: number; y: number; z?: number };
point = {
x: 0,
y: 0,
z: null,
// ~
// 编译错误!类型'null'不能赋值给类型'number | undefined'
};
在非“--strictNullChecks”
模式下,null值与undefined值均可以赋值给可选属性。因为在该模式下,null值与undefined值几乎可以赋值给任意类型。
在操作对象类型的值时,只允许读写对象类型中已经定义的必选属性和可选属性。若访问了未定义的属性,则会产生编译错误。例如,下例中point的类型里没有定义属性t,因此不允许读写属性t
:
let point: { x: number; y: number; z?: number };
// 正确
point = { x: 0, y: 0 };
point.x;
point.y;
// 正确
point = { x: 0, y: 0, z: 0 };
point.x;
point.y;
point.z;
point = { x: 0, y: 0, z: 0, t: 0 }; // 编译错误
point.t; // 编译错误
只读属性
在属性签名定义中添加readonly修饰符能够定义对象只读属性。定义只读属性的语法如下所示:
{
readonly PropertyName: Type;
}
下例中,我们将Point对象类型中的属性x
和属性y
定义为只读属性:
let point: {
readonly x: number;
readonly y: number;
};
point = { x: 0, y: 0 };
只读属性的值在初始化后不允许再被修改,示例如下:
let point: {
readonly x: number;
readonly y: number;
};
// 正确,初始化
point = { x: 0, y: 0 };
point.x = 1;
// ~
// 编译错误!不允许给x赋值,因为它是只读属性
point.y = 1;
// ~
// 编译错误!不允许给y赋值,因为它是只读属性
空对象类型字面量
如果对象类型字面量没有定义任何类型成员,那么它就成了一种特殊的类型,即空对象类型字面量“{}”
。空对象类型字面量表示不带有任何属性的对象类型,因此不允许在“{}”
类型上访问任何自定义属性。示例如下:
const point: {} = { x: 0, y: 0 };
point.x;
// ~
// 编译错误!属性 'x' 不存在于类型 '{}'
point.y;
// ~
// 编译错误!属性 'y' 不存在于类型 '{}'
在空对象类型字面量“{}”
上,允许访问对象公共的属性和方法,也就是Object类型上定义的方法和属性。示例如下:
const point: {} = { x: 0, y: 0 };
point.valueOf();
现在,朋友们可能会发现空对象类型字面量“{}”
与Object类型十分相似。而事实上也正是如此,单从行为上来看两者是可以互换使用的。例如,除了undefined值和null值外,其他任何值都可以赋值给空对象类型字面量“{}”
和Object类型。同时,空对象类型字面量“{}”
和Object类型之间也允许互相赋值。示例如下:
let a: Object = 'hi';
let b: {} = 'hi';
a = b;
b = a;
两者的区别主要在于语义上。全局的Object类型用于描述对象公共的属性和方法,它相当于一种专用类型,因此程序中不应该将自定义变量、参数等类型直接声明为Object类型。空对象类型字面量“{}”
强调的是不包含属性的对象类型,同时也可以作为Object类型的代理来使用。最后,也要注意在某些场景中新的object类型可能是更加合适的选择。
酷客网相关文章:
评论前必须登录!
注册