第一個例子
下面的代碼定義了一個Person
類型
interface Person {
name: string;
age: number;
}
然后又定義了一個函數打印這個類型的對象
function printPerson(person: Person) {
console.log(`Name: ${person.name}, Age: ${person.age}`);
}
按道理來說,要調用這個函數,必須傳遞一個Person
類型的對象,但是你會發現,直接傳一個對象進去也行。
printPerson({ name: "Alice", age: 30 });
這段代碼沒有報錯,為什么呢?因為typescript的結構化類型系統認為,只要傳入的對象包含了Person
類型所需的所有屬性,就可以被認為是Person
類型。你甚至可以多加一些屬性,比如:
printPerson({ name: "Alice", age: 30, location: "Wonderland" });
代碼一樣可以正常運行!
為什么?因為在typescript中,類型是基于結構
的,而不是基于名稱的。只要對象的結構符合要求,就可以被認為是該類型。如果一個類型A包含了類型B的所有屬性,那么類型A就可以被認為是類型B。在使用類型B的地方,就可以使用類型A代替。
第二個例子
還是以上面的Person
類型為例,假設我們要打印Person
對象中的所有屬性,有的同學可能不假思索的寫下如下代碼:
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);
但是這段代碼卻報錯了:
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'.
當我第一次看到這個錯誤時,我只想撞墻,我哪里用any
了,這不是胡扯嗎?但這不是對待錯誤的正確態度,這種錯誤如果不徹底解決,那么它就會一直困擾你,只有將它徹底擊敗,下次再遇到時才能得心應手!
仔細看一下這個報錯,它大概描述了兩件事情:
string
類型的值不能用來索引Person
類型。Person
類型沒有定義索引簽名。
其實這兩件事本質上說的是一個問題,那就是在TypeScript中,只有在類型中顯式定義了索引簽名,才能使用string
類型的值來索引該類型。那么我們就給Person
類型添加一個索引簽名:
方式一:為Person
類型添加索引簽名
interface Person {
name: string;
age: number;
[key: string]: any;
}
[key: string]: any;
這行代碼的意思是,Person
類型可以有任意數量的屬性,屬性名必須是字符串類型 ([key: string]
),屬性值可以是任意類型(any
)。
現在我們再來運行printProperties
函數,就不會報錯了。
方式二:使用keyof
關鍵字
坦白的說,為了一個遍歷函數給Person
類型添加一個索引簽名有點過于冗余了,其實我們可以使用另一個方法來解決這個問題,那就是使用keyof
關鍵字來獲取Person
類型的所有屬性名。
function printProperties(person: Person) {
for (const property in person) {
console.log(`${property}: ${person[property as keyof typeof person]}`);
}
}
來看這一句代碼property as keyof typeof person
, 它的執行步驟是這樣的:
- 先執行
typeof person
,得到Person
類型。 - 再執行
keyof Person
,得到Person
類型的所有屬性名的聯合類型 - name | age
。 - 最后使用
as
操作符將property
轉換為這個聯合類型。
這樣做的好處是,property
的類型被限制為Person
類型的屬性名,在本例中就是name
和age
這兩個屬性,絕不會超出這個范圍,這樣就可以安全地索引person
對象了。
眼力好的同學可能已經發現了,上面這個寫法可以簡化一下,property as keyof typeof person
可以簡化為property as keyof Person
,因為person
的類型就是Person
,所以我們可以直接使用Person
類型來代替。這樣可以節省一個typeof
操作符的使用。
方式三:使用Object.entries
當然,我們還可以使用Object.entries
方法來遍歷對象的屬性,這樣就不需要擔心索引簽名的問題了。
function printProperty(person: Person) {
Object.entries(person).forEach(([key, value]) => {
console.log(`${key}: ${value}`);
});
}
分析一下這段代碼:
Object.entries
方法會返回一個二維數組,其中每個元素又是一個數組,這個數組包含了對象的屬性名和屬性值。以上面的person
對象為例,Object.entries(person)
會返回[['name', 'Alice'], ['age', 30]]
,- 接下來的
forEach
方法會遍歷這個數組,這里使用了一個數組解構操作符([key, value])
,將每個屬性的名字賦值給key,屬性的值賦值給value, - 最后使用
console.log
打印出來。
?轉自https://www.cnblogs.com/graphics/p/18968833
該文章在 2025/7/7 8:28:05 編輯過