關於TypeScript的類型:爲什麼在Vue中[]被推導爲never[],{}被推導爲any

爲什麼[]被推導爲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.

如果某個東西長得像鴨子,像鴨子一樣游泳,像鴨子一樣嘎嘎叫,那它就可以被看成是一隻鴨子。在動態語言中,我們會更關注對象的行爲,而非對象本身的類型。所以,這裏也是有這種鴨子類型的含義在裏面:一個空接口沒有任何行爲,相當於所有類型都實現了它;它變成了所有類型的父類。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章