一、使用 object 類型進行類型聲明
隨着 TypeScript 2.2 的發布,標准庫的類型聲明已經更新,以使用新的對象類型。例如,Object.create() 和Object.setPrototypeOf() 方法,現在需要為它們的原型參數指定 object | null 類型:
// node_modules/typescript/lib/lib.es5.d.ts
interface ObjectConstructor {
create(o: object | null): any;
setPrototypeOf(o: any, proto: object | null): any;
// ...
}
1
2
3
4
5
6
將原始類型作為原型傳遞給 Object.setPrototypeOf() 或 Object.create() 將導致在運行時拋出類型錯誤。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
1
2
3
4
5
6
7
8
9
object 類型的另一個用例是作為 ES2015 的一部分引入的 WeakMap 數據結構。它的鍵必須是對象,不能是原始值。這個要求現在反映在類型定義中:
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;
}
1
2
3
4
5
6
二、Object vs object vs {}
也許令人困惑的是,TypeScript 定義了幾個類型,它們有相似的名字,但是代表不同的概念:
object
Object
{}
我們已經看到了上面的新對象類型。現在讓我們討論 Object 和 {} 表示什么。
2.1 Object 類型
TypeScript 定義了另一個與新的 object 類型幾乎同名的類型,那就是 Object 類型。該類型是所有 Object 類的實例的類型。它由以下兩個接口來定義:
Object 接口定義了 Object.prototype 原型對象上的屬性;
ObjectConstructor 接口定義了 Object 類的屬性。
下面我們來看一下上述兩個接口的相關定義:
1、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;
}
1
2
3
4
5
6
7
8
9
10
11
2、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;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Object 類的所有實例都繼承了 Object 接口中的所有屬性。我們可以看到,如果我們創建一個返回其參數的函數:
傳入一個 Object 對象的實例,它總是會滿足該函數的返回類型 —— 即要求返回值包含一個 toString() 方法。
// Object: Provides functionality common to all JavaScript objects.
function f(x: Object): { toString(): string } {
return x; // OK
}
1
2
3
4
而 object 類型,它用於表示非原始類型(undefined, null, boolean, number, bigint, string, symbol)。使用這種類型,我們不能訪問值的任何屬性。
2.2 Object vs object
有趣的是,類型 Object 包括原始值:
function func1(x: Object) { }
func1('semlinker'); // OK
1
2
為什么?Object.prototype 的屬性也可以通過原始值訪問:
> 'semlinker'.hasOwnProperty === Object.prototype.hasOwnProperty
true
1
2
感興趣的讀者,可以自行了解一下 “JavaScript 裝箱和拆箱” 的相關內容。
相反,object 類型不包括原始值:
function func2(x: object) { }
// Argument of type '"semlinker"'
// is not assignable to parameter of type 'object'.(2345)
func2('semlinker'); // Error
1
2
3
4
5
需要注意的是,當對 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
};
1
2
3
4
5
6
而對於 object 類型來說,TypeScript 編譯器不會提示任何錯誤:
const obj2: object = {
toString() { return 123 }
};
1
2
3
另外在處理 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
1
2
3
4
5
在上述例子中,最后一行會出現編譯錯誤,這是因為 { [key: string]: string } 類型相比 object 類型更加精確。而 header = strictTypeHeaders; 這一行卻沒有提示任何錯誤,是因為這兩種類型都是非基本類型,object 類型比 { [key: string]: string } 類型更加通用。
2.3 空類型 {}
還有另一種類型與之非常相似,即空類型:{}。它描述了一個沒有成員的對象。當你試圖訪問這樣一個對象的任意屬性時,TypeScript 會產生一個編譯時錯誤:
// Type {}
const obj = {};
// Error: Property 'prop' does not exist on type '{}'.
obj.prop = "semlinker";
1
2
3
4
5
但是,你仍然可以使用在 Object 類型上定義的所有屬性和方法,這些屬性和方法可通過 JavaScript 的原型鏈隱式地使用:
// Type {}
const obj = {};
// "[object Object]"
obj.toString();
1
2
3
4
5
在 JavaScript 中創建一個表示二維坐標點的對象很簡單:
const pt = {};
pt.x = 3;
pt.y = 4;
1
2
3
然而以上代碼在 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
1
2
3
4
5
這是因為第 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;
1
2
3
4
5
6
7
8
9
10
很可惜對於以上的方案,TypeScript 編譯器仍會提示錯誤。那么這個問題該如何解決呢?其實我們可以直接通過對象字面量進行賦值:
const pt = {
x: 3,
y: 4,
}; // OK
1
2
3
4
而如果你需要一步一步地創建對象,你可以使用類型斷言(as)來消除 TypeScript 的類型檢查:
const pt = {} as Point;
pt.x = 3;
pt.y = 4; // OK
1
2
3
但是更好的方法是聲明變量的類型並一次性構建對象:
const pt: Point = {
x: 3,
y: 4,
};
1
2
3
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
1
2
3
4
5
6
7
這時候你可以使用對象展開運算符 … 來解決上述問題:
const pt = { x: 666, y: 888 };
const id = { name: "semlinker" };
const namedPoint = {...pt, ...id}
//(property) name: string
namedPoint.name // Ok
1
2
3
4
5
6
三、對象字面量類型 vs 接口類型
我們除了可以通過 Object 和 object 類型來描述對象之外,也可以通過對象的屬性來描述對象
// Object literal type
let obj3: { prop: boolean };
// Interface
interface ObjectType {
prop: boolean;
}
let obj4: ObjectType;
1
2
3
4
5
6
7
8
9
在 TypeScript 中有兩種定義對象類型的方法,它們非常相似:
// Object literal type
type ObjType1 = {
a: boolean,
b: number;
c: string,
};
// Interface
interface ObjType2 {
a: boolean,
b: number;
c: string,
}
1
2
3
4
5
6
7
8
9
10
11
12
13
在以上代碼中,我們使用分號或逗號作為分隔符。尾隨分隔符是允許的,也是可選的。好的,那么現在問題來了,對象字面量類型和接口類型之間有什么區別呢?下面我從以下幾個方面來分析一下它們之間的區別:
3.1 內聯
對象字面量類型可以內聯,而接口不能:
// Inlined object literal type:
function f1(x: { prop: number }) {}
function f2(x: ObjectInterface) {} // referenced interface
interface ObjectInterface {
prop: number;
}
1
2
3
4
5
6
7
3.2 名稱重復
含有重復名稱的類型別名是非法的:
// @ts-ignore: Duplicate identifier 'PersonAlias'. (2300)
type PersonAlias = {first: string};
// @ts-ignore: Duplicate identifier 'PersonAlias'. (2300)
type PersonAlias = {last: string};
1
2
3
4
5
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',
};
1
2
3
4
5
6
7
8
9
10
11
12
3.3 映射類型
對於映射類型(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];
// };
1
2
3
4
5
6
7
8
9
10
11
12
13
3.4 多態 this 類型
多態 this 類型僅適用於接口:
interface AddsStrings {
add(str: string): this;
};
class StringBuilder implements AddsStrings {
result = '';
add(str: string) {
this.result += str;
return this;
}
}
1
2
3
4
5
6
7
8
9
10
11
四、總結
相信很多剛接觸 TypeScript 的讀者,看到 Object、object 和 {} 這幾種類型時,也會感到疑惑。因為不知道它們之間的有什么區別,什么時候使用?為了讓讀者能更直觀的了解到它們之間的區別,最后我們來做個總結:
4.1 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
4.2 Object 類型
Object 類型:它是所有 Object 類的實例的類型。它由以下兩個接口來定義:
它由以下兩個接口來定義:
Object 接口定義了 Object.prototype 原型對象上的屬性;
// 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;
}
1
2
3
4
5
6
7
8
9
10
11
ObjectConstructor 接口定義了 Object 類的屬性。
// 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;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Object 類的所有實例都繼承了 Object 接口中的所有屬性。
4.3 {} 類型
{} 類型:它描述了一個沒有成員的對象。當你試圖訪問這樣一個對象的任意屬性時,TypeScript 會產生一個編譯時錯誤。
// Type {}
const obj = {};
// Error: Property 'prop' does not exist on type '{}'.
obj.prop = "semlinker";
1
2
3
4
5
但是,你仍然可以使用在 Object 類型上定義的所有屬性和方法。
————————————————
版權聲明:本文為CSDN博主「金剛腿」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/GoldenLegs/article/details/112965682