TypeScript对象类型字面量

对象类型字面量是定义对象类型的方法之一。下例中,我们使用对象类型字面量定义了一个对象类型。该对象类型中包含了两个属性成员xy,它们的类型均为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类型可能是更加合适的选择。

酷客网相关文章:

赞(0)

评论 抢沙发

评论前必须登录!