第一個(gè)例子
下面的代碼定義了一個(gè)Person
類型
interface Person {
name: string;
age: number;
}
然后又定義了一個(gè)函數(shù)打印這個(gè)類型的對(duì)象
function printPerson(person: Person) {
console.log(`Name: ${person.name}, Age: ${person.age}`);
}
按道理來說,要調(diào)用這個(gè)函數(shù),必須傳遞一個(gè)Person
類型的對(duì)象,但是你會(huì)發(fā)現(xiàn),直接傳一個(gè)對(duì)象進(jìn)去也行。
printPerson({ name: "Alice", age: 30 });
這段代碼沒有報(bào)錯(cuò),為什么呢?因?yàn)閠ypescript的結(jié)構(gòu)化類型系統(tǒng)認(rèn)為,只要傳入的對(duì)象包含了Person
類型所需的所有屬性,就可以被認(rèn)為是Person
類型。你甚至可以多加一些屬性,比如:
printPerson({ name: "Alice", age: 30, location: "Wonderland" });
代碼一樣可以正常運(yùn)行!
為什么?因?yàn)樵趖ypescript中,類型是基于結(jié)構(gòu)
的,而不是基于名稱的。只要對(duì)象的結(jié)構(gòu)符合要求,就可以被認(rèn)為是該類型。如果一個(gè)類型A包含了類型B的所有屬性,那么類型A就可以被認(rèn)為是類型B。在使用類型B的地方,就可以使用類型A代替。
第二個(gè)例子
還是以上面的Person
類型為例,假設(shè)我們要打印Person
對(duì)象中的所有屬性,有的同學(xué)可能不假思索的寫下如下代碼:
interface Person {
name: string;
age: number;
}
const person: Person = { name: "Alice", age: 30 };
function printProperties(person: Person) {
for (const property in person) {
console.log(`${property}: ${person[property]}`);
}
}
printProperties(person);
但是這段代碼卻報(bào)錯(cuò)了:
TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Person'. No index signature with a parameter of type 'string' was found on type 'Person'.
當(dāng)我第一次看到這個(gè)錯(cuò)誤時(shí),我只想撞墻,我哪里用any
了,這不是胡扯嗎?但這不是對(duì)待錯(cuò)誤的正確態(tài)度,這種錯(cuò)誤如果不徹底解決,那么它就會(huì)一直困擾你,只有將它徹底擊敗,下次再遇到時(shí)才能得心應(yīng)手!
仔細(xì)看一下這個(gè)報(bào)錯(cuò),它大概描述了兩件事情:
string
類型的值不能用來索引Person
類型。Person
類型沒有定義索引簽名。
其實(shí)這兩件事本質(zhì)上說的是一個(gè)問題,那就是在TypeScript中,只有在類型中顯式定義了索引簽名,才能使用string
類型的值來索引該類型。那么我們就給Person
類型添加一個(gè)索引簽名:
方式一:為Person
類型添加索引簽名
interface Person {
name: string;
age: number;
[key: string]: any;
}
[key: string]: any;
這行代碼的意思是,Person
類型可以有任意數(shù)量的屬性,屬性名必須是字符串類型 ([key: string]
),屬性值可以是任意類型(any
)。
現(xiàn)在我們?cè)賮磉\(yùn)行printProperties
函數(shù),就不會(huì)報(bào)錯(cuò)了。
方式二:使用keyof
關(guān)鍵字
坦白的說,為了一個(gè)遍歷函數(shù)給Person
類型添加一個(gè)索引簽名有點(diǎn)過于冗余了,其實(shí)我們可以使用另一個(gè)方法來解決這個(gè)問題,那就是使用keyof
關(guān)鍵字來獲取Person
類型的所有屬性名。
function printProperties(person: Person) {
for (const property in person) {
console.log(`${property}: ${person[property as keyof typeof person]}`);
}
}
來看這一句代碼property as keyof typeof person
, 它的執(zhí)行步驟是這樣的:
- 先執(zhí)行
typeof person
,得到Person
類型。 - 再執(zhí)行
keyof Person
,得到Person
類型的所有屬性名的聯(lián)合類型 - name | age
。 - 最后使用
as
操作符將property
轉(zhuǎn)換為這個(gè)聯(lián)合類型。
這樣做的好處是,property
的類型被限制為Person
類型的屬性名,在本例中就是name
和age
這兩個(gè)屬性,絕不會(huì)超出這個(gè)范圍,這樣就可以安全地索引person
對(duì)象了。
眼力好的同學(xué)可能已經(jīng)發(fā)現(xiàn)了,上面這個(gè)寫法可以簡(jiǎn)化一下,property as keyof typeof person
可以簡(jiǎn)化為property as keyof Person
,因?yàn)?code style="font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace, sans-serif; padding: 0px 5px; line-height: 1.8; margin: 0px 3px; display: inline-block; overflow-x: auto; vertical-align: middle; border-radius: 3px; background-color: rgb(251, 229, 225); color: rgb(192, 52, 29); border: none !important;">person的類型就是Person
,所以我們可以直接使用Person
類型來代替。這樣可以節(jié)省一個(gè)typeof
操作符的使用。
方式三:使用Object.entries
當(dāng)然,我們還可以使用Object.entries
方法來遍歷對(duì)象的屬性,這樣就不需要擔(dān)心索引簽名的問題了。
function printProperty(person: Person) {
Object.entries(person).forEach(([key, value]) => {
console.log(`${key}: ${value}`);
});
}
分析一下這段代碼:
Object.entries
方法會(huì)返回一個(gè)二維數(shù)組,其中每個(gè)元素又是一個(gè)數(shù)組,這個(gè)數(shù)組包含了對(duì)象的屬性名和屬性值。以上面的person
對(duì)象為例,Object.entries(person)
會(huì)返回[['name', 'Alice'], ['age', 30]]
,- 接下來的
forEach
方法會(huì)遍歷這個(gè)數(shù)組,這里使用了一個(gè)數(shù)組解構(gòu)操作符([key, value])
,將每個(gè)屬性的名字賦值給key,屬性的值賦值給value, - 最后使用
console.log
打印出來。
?轉(zhuǎn)自https://www.cnblogs.com/graphics/p/18968833
該文章在 2025/7/7 8:28:05 編輯過