爲什麼[]被推導爲never[]
2020.03.20 修改錯別字,採用更清晰的表述
可能用“原生”Vue 2.x + ts(不使用vue-property-decorator)的同學或多或少都遇到過這樣的問題。比如,在data()
裏聲明一個數組,賦值的時候會報錯 TS2322: Type ‘number’ is not assignable to type ‘never’:
data () {
return {
foo: []
}
},
mounted () {
this.foo = [{bar: 1}] // TS2322: Type 'number' is not assignable to type 'never'.
}
也就是說,這裏的空數組foo的類型被推導成了never[]
。爲什麼?先來看看官方對never
類型的定義:
The
never
type represents the type of values that never occur.
也就是說,never是永遠不會出現的值的類型。一個聲明爲空的數組裏不應該出現任何值,所以被推導爲never也是合理的。相當於你向編譯器表示,我這個數組是空的,編譯器自然就認爲這個數組一直是空的,此時你要給他賦值,顯然會破壞類型系統。
那麼,爲什麼foo不就是一個普通的數組,而會進行推導?因爲這是在函數的返回值中,沒有顯式聲明,編譯器會進行自動推導。如果想讓這個空數組符合它原來的含義,需要進行類型斷言:
data () {
return {
foo: [] as []
}
}
空數組和never[]
還是有很大區別的。
爲什麼{}被推導爲any
按照之前的結論,可能會有人認爲{}也會被推導成類似於{[key: string] : never}
這樣的類型。但是,事實上{}
會被推導成any,任何類型都可以賦值給它:
data () {
return {
foo: {}
}
},
mounted () {
this.foo = {bar: 1}
this.foo = 1
}
至於原因,讓我們看看TypeScript的官方wiki是怎麼說的:
Types with no members can be substituted by any type.
In general, you should never find yourself declaring an
interface
with no properties.
也就是說,任何類型都可以賦值給沒有成員的類型,相當於變成了any
。
更深層次的原因是動態語言的鴨子類型:
If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.
如果某個東西長得像鴨子,像鴨子一樣游泳,像鴨子一樣嘎嘎叫,那它就可以被看成是一隻鴨子。在動態語言中,我們會更關注對象的行爲,而非對象本身的類型。所以,這裏也是有這種鴨子類型的含義在裏面:一個空接口沒有任何行爲,相當於所有類型都實現了它;它變成了所有類型的父類。