TypeScript 2.2 引入了被称为 object
类型的新类型,它用于表示非原始类型。在 JavaScript 中如下类型被视为原始类型:string
、boolean
、number
、bigint
、symbol
、null
和 undefined
。javascript
全部其余类型均被视为非基本类型。新的 object
类型表示以下:html
// All primitive types
type Primitive = string
| boolean | number
| bigint | symbol
| null | undefined;
// All non-primitive types
type NonPrimitive = object;
复制代码
让咱们看看 object
类型,如何让咱们编写更精确的类型声明。java
随着 TypeScript 2.2 的发布,标准库的类型声明已经更新,以使用新的对象类型。例如,Object.create()
和Object.setPrototypeOf()
方法,如今须要为它们的原型参数指定 object | null
类型:node
// node_modules/typescript/lib/lib.es5.d.ts
interface ObjectConstructor {
create(o: object | null): any;
setPrototypeOf(o: any, proto: object | null): any;
// ...
}
复制代码
将原始类型做为原型传递给 Object.setPrototypeOf()
或 Object.create()
将致使在运行时抛出类型错误。TypeScript 如今可以捕获这些错误,并在编译时提示相应的错误:typescript
const proto = {};
Object.create(proto); // OK
Object.create(null); // OK
Object.create(undefined); // Error
Object.create(1337); // Error
Object.create(true); // Error
Object.create("oops"); // Error
复制代码
object
类型的另外一个用例是做为 ES2015 的一部分引入的 WeakMap 数据结构。它的键必须是对象,不能是原始值。这个要求如今反映在类型定义中:shell
interface WeakMap<K extends object, V> {
delete(key: K): boolean;
get(key: K): V | undefined;
has(key: K): boolean;
set(key: K, value: V): this;
}
复制代码
也许使人困惑的是,TypeScript 定义了几个类型,它们有类似的名字,可是表明不一样的概念:bash
object
Object
{}
咱们已经看到了上面的新对象类型。如今让咱们讨论 Object
和 {}
表示什么。微信
TypeScript 定义了另外一个与新的 object
类型几乎同名的类型,那就是 Object
类型。该类型是全部 Object 类的实例的类型。它由如下两个接口来定义:数据结构
下面咱们来看一下上述两个接口的相关定义:ide
一、Object
接口定义
// node_modules/typescript/lib/lib.es5.d.ts
interface Object {
constructor: Function;
toString(): string;
toLocaleString(): string;
valueOf(): Object;
hasOwnProperty(v: PropertyKey): boolean;
isPrototypeOf(v: Object): boolean;
propertyIsEnumerable(v: PropertyKey): boolean;
}
复制代码
二、ObjectConstructor
接口定义
// node_modules/typescript/lib/lib.es5.d.ts
interface ObjectConstructor {
/** Invocation via `new` */
new(value?: any): Object;
/** Invocation via function calls */
(value?: any): any;
readonly prototype: Object;
getPrototypeOf(o: any): any;
// ···
}
declare var Object: ObjectConstructor;
复制代码
Object 类的全部实例都继承了 Object 接口中的全部属性。咱们能够看到,若是咱们建立一个返回其参数的函数:
传入一个 Object 对象的实例,它老是会知足该函数的返回类型 —— 即要求返回值包含一个 toString() 方法。
// Object: Provides functionality common to all JavaScript objects.
function f(x: Object): { toString(): string } {
return x; // OK
}
复制代码
而 object
类型,它用于表示非原始类型(undefined, null, boolean, number, bigint, string, symbol)。使用这种类型,咱们不能访问值的任何属性。
有趣的是,类型 Object
包括原始值:
function func1(x: Object) { }
func1('semlinker'); // OK
复制代码
为何?Object.prototype
的属性也能够经过原始值访问:
> 'semlinker'.hasOwnProperty === Object.prototype.hasOwnProperty
true
复制代码
感兴趣的读者,能够自行了解一下 “JavaScript 装箱和拆箱” 的相关内容。
相反,object
类型不包括原始值:
function func2(x: object) { }
// Argument of type '"semlinker"'
// is not assignable to parameter of type 'object'.(2345)
func2('semlinker'); // Error
复制代码
须要注意的是,当对 Object 类型的变量进行赋值时,若是值对象属性名与 Object 接口中的属性冲突,则 TypeScript 编译器会提示相应的错误:
// Type '() => number' is not assignable to type
// '() => string'.
// Type 'number' is not assignable to type 'string'.
const obj1: Object = {
toString() { return 123 } // Error
};
复制代码
而对于 object 类型来讲,TypeScript 编译器不会提示任何错误:
const obj2: object = {
toString() { return 123 }
};
复制代码
另外在处理 object 类型和字符串索引对象类型的赋值操做时,也要特别注意。好比:
let strictTypeHeaders: { [key: string]: string } = {};
let header: object = {};
header = strictTypeHeaders; // OK
// Type 'object' is not assignable to type '{ [key: string]: string; }'.
strictTypeHeaders = header; // Error
复制代码
在上述例子中,最后一行会出现编译错误,这是由于 { [key: string]: string }
类型相比 object
类型更加精确。而 header = strictTypeHeaders;
这一行却没有提示任何错误,是由于这两种类型都是非基本类型,object
类型比 { [key: string]: string }
类型更加通用。
还有另外一种类型与之很是类似,即空类型:{}
。它描述了一个没有成员的对象。当你试图访问这样一个对象的任意属性时,TypeScript 会产生一个编译时错误:
// Type {}
const obj = {};
// Error: Property 'prop' does not exist on type '{}'.
obj.prop = "semlinker";
复制代码
可是,你仍然可使用在 Object 类型上定义的全部属性和方法,这些属性和方法可经过 JavaScript 的原型链隐式地使用:
// Type {}
const obj = {};
// "[object Object]"
obj.toString();
复制代码
在 JavaScript 中建立一个表示二维坐标点的对象很简单:
const pt = {};
pt.x = 3;
pt.y = 4;
复制代码
然而以上代码在 TypeScript 中,每一个赋值语句都会产生错误:
const pt = {}; // (A)
// Property 'x' does not exist on type '{}'
pt.x = 3; // Error
// Property 'y' does not exist on type '{}'
pt.y = 4; // Error
复制代码
这是由于第 A 行中的 pt 类型是根据它的值 {} 推断出来的,你只能够对已知的属性赋值。这个问题怎么解决呢?有些读者可能会先想到接口,好比这样子:
interface Point {
x: number;
y: number;
}
// Type '{}' is missing the following
// properties from type 'Point': x, y(2739)
const pt: Point = {}; // Error
pt.x = 3;
pt.y = 4;
复制代码
很惋惜对于以上的方案,TypeScript 编译器仍会提示错误。那么这个问题该如何解决呢?其实咱们能够直接经过对象字面量进行赋值:
const pt = {
x: 3,
y: 4,
}; // OK
复制代码
而若是你须要一步一步地建立对象,你可使用类型断言(as)来消除 TypeScript 的类型检查:
const pt = {} as Point;
pt.x = 3;
pt.y = 4; // OK
复制代码
可是更好的方法是声明变量的类型并一次性构建对象:
const pt: Point = {
x: 3,
y: 4,
};
复制代码
另外在使用 Object.assign
方法合并多个对象的时候,你可能也会遇到如下问题:
const pt = { x: 666, y: 888 };
const id = { name: "semlinker" };
const namedPoint = {};
Object.assign(namedPoint, pt, id);
// Property 'name' does not exist on type '{}'.(2339)
namedPoint.name; // Error
复制代码
这时候你可使用对象展开运算符 ...
来解决上述问题:
const pt = { x: 666, y: 888 };
const id = { name: "semlinker" };
const namedPoint = {...pt, ...id}
//(property) name: string
namedPoint.name // Ok
复制代码
咱们除了能够经过 Object 和 object 类型来描述对象以外,也能够经过对象的属性来描述对象:
// Object literal type
let obj3: { prop: boolean };
// Interface
interface ObjectType {
prop: boolean;
}
let obj4: ObjectType;
复制代码
在 TypeScript 中有两种定义对象类型的方法,它们很是类似:
// Object literal type
type ObjType1 = {
a: boolean,
b: number;
c: string,
};
// Interface
interface ObjType2 {
a: boolean,
b: number;
c: string,
}
复制代码
在以上代码中,咱们使用分号或逗号做为分隔符。尾随分隔符是容许的,也是可选的。好的,那么如今问题来了,对象字面量类型和接口类型之间有什么区别呢?下面我从如下几个方面来分析一下它们之间的区别:
对象字面量类型能够内联,而接口不能:
// Inlined object literal type:
function f1(x: { prop: number }) {}
function f2(x: ObjectInterface) {} // referenced interface
interface ObjectInterface {
prop: number;
}
复制代码
含有重复名称的类型别名是非法的:
// @ts-ignore: Duplicate identifier 'PersonAlias'. (2300)
type PersonAlias = {first: string};
// @ts-ignore: Duplicate identifier 'PersonAlias'. (2300)
type PersonAlias = {last: string};
复制代码
TypeScript 2.6 支持在 .ts 文件中经过在报错一行上方使用
// @ts-ignore
来忽略错误。
// @ts-ignore
注释会忽略下一行中产生的全部错误。建议实践中在@ts-ignore
以后添加相关提示,解释忽略了什么错误。请注意,这个注释仅会隐藏报错,而且咱们建议你少使用这一注释。
相反,含有重复名称的接口将会被合并:
interface PersonInterface {
first: string;
}
interface PersonInterface {
last: string;
}
const sem: PersonInterface = {
first: 'Jiabao',
last: 'Huang',
};
复制代码
对于映射类型(A行),咱们须要使用对象字面量类型:
interface Point {
x: number;
y: number;
}
type PointCopy1 = {
[Key in keyof Point]: Point[Key]; // (A)
};
// Syntax error:
// interface PointCopy2 {
// [Key in keyof Point]: Point[Key];
// };
复制代码
多态 this 类型仅适用于接口:
interface AddsStrings {
add(str: string): this;
};
class StringBuilder implements AddsStrings {
result = '';
add(str: string) {
this.result += str;
return this;
}
}
复制代码
相信不少刚接触 TypeScript 的读者,看到 Object、object 和 {} 这几种类型时,也会感到疑惑。由于不知道它们之间的有什么区别,何时使用?为了让读者能更直观的了解到它们之间的区别,最后咱们来作个总结:
object 类型是:TypeScript 2.2 引入的新类型,它用于表示非原始类型。
// node_modules/typescript/lib/lib.es5.d.ts
interface ObjectConstructor {
create(o: object | null): any;
// ...
}
const proto = {};
Object.create(proto); // OK
Object.create(null); // OK
Object.create(undefined); // Error
Object.create(1337); // Error
Object.create(true); // Error
Object.create("oops"); // Error
复制代码
Object 类型:它是全部 Object 类的实例的类型。它由如下两个接口来定义:
它由如下两个接口来定义:
// node_modules/typescript/lib/lib.es5.d.ts
interface Object {
constructor: Function;
toString(): string;
toLocaleString(): string;
valueOf(): Object;
hasOwnProperty(v: PropertyKey): boolean;
isPrototypeOf(v: Object): boolean;
propertyIsEnumerable(v: PropertyKey): boolean;
}
复制代码
// node_modules/typescript/lib/lib.es5.d.ts
interface ObjectConstructor {
/** Invocation via `new` */
new(value?: any): Object;
/** Invocation via function calls */
(value?: any): any;
readonly prototype: Object;
getPrototypeOf(o: any): any;
// ···
}
declare var Object: ObjectConstructor;
复制代码
Object 类的全部实例都继承了 Object 接口中的全部属性。
{} 类型:它描述了一个没有成员的对象。当你试图访问这样一个对象的任意属性时,TypeScript 会产生一个编译时错误。
// Type {}
const obj = {};
// Error: Property 'prop' does not exist on type '{}'.
obj.prop = "semlinker";
复制代码
可是,你仍然可使用在 Object 类型上定义的全部属性和方法。
建立了一个 “重学TypeScript” 的微信群,想加群的小伙伴,加我微信 "semlinker",备注重学TS。目前已有 TS 系列文章 38 篇。