TS 中 Object, object, {} 類型之間的區(qū)別

TypeScript 2.2 引入了被稱為 object 類型的新類型版保,它用于表示非原始類型。在 JavaScript 中以下類型被視為原始類型:string地淀、boolean嬉探、numberbigint排惨、symbolnullundefined碰凶。

所有其他類型均被視為非基本類型暮芭。新的 object 類型表示如下:

// All primitive types

type Primitive = string

| boolean | number

| bigint | symbol

| null | undefined;

// All non-primitive types

type NonPrimitive = object;

|

讓我們看看 object 類型,如何讓我們編寫更精確的類型聲明欲低。

一辕宏、使用 object 類型進(jìn)行類型聲明

隨著 TypeScript 2.2 的發(fā)布,標(biāo)準(zhǔn)庫的類型聲明已經(jīng)更新砾莱,以使用新的對象類型瑞筐。例如,Object.create()Object.setPrototypeOf() 方法腊瑟,現(xiàn)在需要為它們的原型參數(shù)指定 object | null 類型:

|

// 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() 將導(dǎo)致在運(yùn)行時拋出類型錯誤聚假。TypeScript 現(xiàn)在能夠捕獲這些錯誤块蚌,并在編譯時提示相應(yīng)的錯誤:

|

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 數(shù)據(jù)結(jié)構(gòu)。它的鍵必須是對象魔策,不能是原始值匈子。這個要求現(xiàn)在反映在類型定義中:

|

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;

}

|

二、Object vs object vs {}

也許令人困惑的是闯袒,TypeScript 定義了幾個類型虎敦,它們有相似的名字,但是代表不同的概念:

  • object
  • Object
  • {}

我們已經(jīng)看到了上面的新對象類型≌遥現(xiàn)在讓我們討論 Object{} 表示什么其徙。

2.1 Object 類型

TypeScript 定義了另一個與新的 object 類型幾乎同名的類型,那就是 Object 類型喷户。該類型是所有 Object 類的實(shí)例的類型唾那。它由以下兩個接口來定義:

  • Object 接口定義了 Object.prototype 原型對象上的屬性;
  • ObjectConstructor 接口定義了 Object 類的屬性褪尝。

下面我們來看一下上述兩個接口的相關(guān)定義:

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;

}

|

2、ObjectConstructor 接口定義

|

// node_modules``/typescript/lib/lib``.es5.d.ts

interface ObjectConstructor {

/** Invocation vianew*/

new(value?: any): Object;

/** Invocation via function calls */

(value?: any): any;

readonly prototype: Object;

getPrototypeOf(o: any): any;

// ···

}

declare var Object: ObjectConstructor;

|

Object 類的所有實(shí)例都繼承了 Object 接口中的所有屬性河哑。我們可以看到避诽,如果我們創(chuàng)建一個返回其參數(shù)的函數(shù):

傳入一個 Object 對象的實(shí)例,它總是會滿足該函數(shù)的返回類型 —— 即要求返回值包含一個 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)。使用這種類型佳吞,我們不能訪問值的任何屬性拱雏。

2.2 Object vs object

有趣的是,類型 Object 包括原始值:

|

function func1(x: Object) { }

func1(``'semlinker'``); // OK

|

為什么底扳?Object.prototype 的屬性也可以通過原始值訪問:

|

> 'semlinker'``.hasOwnProperty === Object.prototype.hasOwnProperty

true

|

感興趣的讀者铸抑,可以自行了解一下 “JavaScript 裝箱和拆箱” 的相關(guān)內(nèi)容。

相反衷模,object 類型不包括原始值:

|

function func2(x: object) { }

// Argument of type '"semlinker"'

// is not assignable to parameter of type 'object'``.(2345)

func2(``'semlinker'``); // Error

|

需要注意的是羡滑,當(dāng)對 Object 類型的變量進(jìn)行賦值時,如果值對象屬性名與 Object 接口中的屬性沖突算芯,則 TypeScript 編譯器會提示相應(yīng)的錯誤:

|

// 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

|

在上述例子中,最后一行會出現(xiàn)編譯錯誤氏涩,這是因?yàn)?{ [key: string]: string } 類型相比 object 類型更加精確届囚。而 header = strictTypeHeaders; 這一行卻沒有提示任何錯誤有梆,是因?yàn)檫@兩種類型都是非基本類型,object 類型比 { [key: string]: string } 類型更加通用意系。

2.3 空類型 {}

還有另一種類型與之非常相似泥耀,即空類型:{}。它描述了一個沒有成員的對象蛔添。當(dāng)你試圖訪問這樣一個對象的任意屬性時痰催,TypeScript 會產(chǎn)生一個編譯時錯誤:

|

// Type {}

const obj = {};

// Error: Property 'prop' does not exist on type '{}'``.

obj.prop = "semlinker"``;

|

但是,你仍然可以使用在 Object 類型上定義的所有屬性和方法迎瞧,這些屬性和方法可通過 JavaScript 的原型鏈隱式地使用:

|

// Type {}

const obj = {};

// "[object Object]"

obj.toString();

|

在 JavaScript 中創(chuàng)建一個表示二維坐標(biāo)點(diǎn)的對象很簡單:

|

const pt = {};

pt.x = 3;

pt.y = 4;

|

然而以上代碼在 TypeScript 中夸溶,每個賦值語句都會產(chǎn)生錯誤:

|

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

|

這是因?yàn)榈?A 行中的 pt 類型是根據(jù)它的值 {} 推斷出來的,你只可以對已知的屬性賦值凶硅。這個問題怎么解決呢缝裁?有些讀者可能會先想到接口,比如這樣子:

|

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 編譯器仍會提示錯誤捷绑。那么這個問題該如何解決呢?其實(shí)我們可以直接通過對象字面量進(jìn)行賦值:

|

const pt = {

x: 3,

y: 4,

}; // OK

|

而如果你需要一步一步地創(chuàng)建對象氢妈,你可以使用類型斷言(as)來消除 TypeScript 的類型檢查:

|

const pt = {} as Point;

pt.x = 3;

pt.y = 4; // OK

|

但是更好的方法是聲明變量的類型并一次性構(gòu)建對象:

|

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

|

這時候你可以使用對象展開運(yùn)算符 ... 來解決上述問題:

|

const pt = { x: 666, y: 888 };

const id = { name: "semlinker" };

const namedPoint = {...pt, ...``id``}

//``(property) name: string

namedPoint.name // Ok

|

三、對象字面量類型 vs 接口類型

我們除了可以通過 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,

}

|

在以上代碼中,我們使用分號或逗號作為分隔符蕾总。尾隨分隔符是允許的粥航,也是可選的。好的生百,那么現(xiàn)在問題來了递雀,對象字面量類型和接口類型之間有什么區(qū)別呢?下面我從以下幾個方面來分析一下它們之間的區(qū)別:

3.1 內(nèi)聯(lián)

對象字面量類型可以內(nèi)聯(lián)蚀浆,而接口不能:

|

// Inlined object literal type``:

function f1(x: { prop: number }) {}

function f2(x: ObjectInterface) {} // referenced interface

interface ObjectInterface {

prop: number;

}

|

3.2 名稱重復(fù)

含有重復(fù)名稱的類型別名是非法的:

|

// @ts-ignore: Duplicate identifier 'PersonAlias'``. (2300)

type PersonAlias = {first: string};

// @ts-ignore: Duplicate identifier 'PersonAlias'``. (2300)

type PersonAlias = {last: string};

|

TypeScript 2.6 支持在 .ts 文件中通過在報(bào)錯一行上方使用
// @ts-ignore 來忽略錯誤缀程。

// @ts-ignore 注釋會忽略下一行中產(chǎn)生的所有錯誤。建議實(shí)踐中在 @ts-ignore之后添加相關(guān)提示市俊,解釋忽略了什么錯誤杨凑。

請注意,這個注釋僅會隱藏報(bào)錯摆昧,并且我們建議你少使用這一注釋撩满。

相反,含有重復(fù)名稱的接口將會被合并:

|

interface PersonInterface {

first: string;

}

interface PersonInterface {

last: string;

}

const sem: PersonInterface = {

first: 'Jiabao'``,

last: 'Huang'``,

};

|

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];

// };

|

3.4 多態(tài) this 類型

多態(tài) this 類型僅適用于接口:

|

interface AddsStrings {

add(str: string): this;

};

class StringBuilder implements AddsStrings {

result = ''``;

add(str: string) {

this.result += str;

return this;

}

}

|

四伺帘、總結(jié)

相信很多剛接觸 TypeScript 的讀者昭躺,看到 Object、object 和 {} 這幾種類型時伪嫁,也會感到疑惑领炫。因?yàn)椴恢浪鼈冎g的有什么區(qū)別,什么時候使用张咳?為了讓讀者能更直觀的了解到它們之間的區(qū)別帝洪,最后我們來做個總結(jié):

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

|

4.2 Object 類型

Object 類型:它是所有 Object 類的實(shí)例的類型碟狞。它由以下兩個接口來定義:

它由以下兩個接口來定義:

  • 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;

}

|

  • ObjectConstructor 接口定義了 Object 類的屬性婚陪。

|

// node_modules``/typescript/lib/lib``.es5.d.ts

interface ObjectConstructor {

/** Invocation vianew*/

new(value?: any): Object;

/** Invocation via function calls */

(value?: any): any;

readonly prototype: Object;

getPrototypeOf(o: any): any;

// ···

}

declare var Object: ObjectConstructor;

|

Object 類的所有實(shí)例都繼承了 Object 接口中的所有屬性族沃。

4.3 {} 類型

{} 類型:它描述了一個沒有成員的對象。當(dāng)你試圖訪問這樣一個對象的任意屬性時泌参,TypeScript 會產(chǎn)生一個編譯時錯誤脆淹。

|

// Type {}

const obj = {};

// Error: Property 'prop' does not exist on type '{}'``.

obj.prop = "semlinker"``;

|

但是,你仍然可以使用在 Object 類型上定義的所有屬性和方法沽一。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末盖溺,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子铣缠,更是在濱河造成了極大的恐慌烘嘱,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蝗蛙,死亡現(xiàn)場離奇詭異蝇庭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)捡硅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進(jìn)店門哮内,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人壮韭,你說我怎么就攤上這事北发。” “怎么了喷屋?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵琳拨,是天一觀的道長。 經(jīng)常有香客問我屯曹,道長从绘,這世上最難降的妖魔是什么寄疏? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮僵井,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘驳棱。我一直安慰自己批什,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布社搅。 她就那樣靜靜地躺著驻债,像睡著了一般。 火紅的嫁衣襯著肌膚如雪形葬。 梳的紋絲不亂的頭發(fā)上合呐,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天,我揣著相機(jī)與錄音笙以,去河邊找鬼淌实。 笑死,一個胖子當(dāng)著我的面吹牛猖腕,可吹牛的內(nèi)容都是我干的拆祈。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼倘感,長吁一口氣:“原來是場噩夢啊……” “哼放坏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起老玛,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤淤年,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蜡豹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體麸粮,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年余素,在試婚紗的時候發(fā)現(xiàn)自己被綠了豹休。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡桨吊,死狀恐怖威根,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情视乐,我是刑警寧澤洛搀,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站佑淀,受9級特大地震影響留美,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一谎砾、第九天 我趴在偏房一處隱蔽的房頂上張望逢倍。 院中可真熱鬧,春花似錦景图、人聲如沸较雕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽亮蒋。三九已至,卻和暖如春妆毕,著一層夾襖步出監(jiān)牢的瞬間慎玖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工笛粘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留趁怔,地道東北人。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓闰蛔,卻偏偏與公主長得像痕钢,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子序六,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容