getLength,它似乎作業
這兩個函式在我看來基本相同(第二個函式可能更通用,因為它可以接受屬性超出陣列中的屬性的物件):
在運行時,它們是相同的 javascript。
function getLength<T>(
// v is an array of some type T
// This could be the type 'any',
// so I know nothing about what's inside
v: T[]
): number {
return v.length;
}
function getLength2<T extends any[]>(
// v is an array, I know nothing
// about what's inside
v: T
): number {
return v.length;
}
flattenArray……啊,好吧,真的不行
但是,如果我在這里進行相同的替換,則會開始看到型別錯誤。
function flattenArray<T>(
a:T[][]
) : T[] {
return a.reduce((prev:T[], curr:T[]) => [...prev,...curr], [] as T[]);
}
function flattenArray<T extends any[]>(
a:T[]
) : T {
return a.reduce((prev:T, curr:T) => [...prev,...curr], [] as T);
}
錯誤:
Type 'T[number][]' is not assignable to type 'T'Conversion of type 'never[]' to type 'T' may be a mistake
據我所知,我可以對扁平化的結果陣列進行型別轉換,并且可以完全繞過型別系統來創建空陣列。
function flattenArray<T extends any[]>(
a:T[]
) : T {
return a.reduce((prev:T, curr:T) => [...prev,...curr] as T, [] as unknown as T);
}
這對我來說似乎是一個危險信號。我懷疑我對型別系統的作業方式有一些不了解。當我說 時T extends any[],我認為 T 至少具有陣列的所有屬性。它可能有更多,但不會更少。所以我可以像使用陣列一樣使用 T。
任何見解都會有所幫助!
uj5u.com熱心網友回復:
正如您所指出的,這T extends any[]意味著T必須可分配給陣列型別,但允許具有額外的屬性(根據結構型別的要求)。這對于函式的輸入來說不是問題,但不能由輸出來保證。舉個例子:
function flattenArrayBad<T extends any[]>(a: T[]): T {
return a.reduce<T>((prev, curr) => [...prev, ...curr], []);
// ------------------------------> ~~~~~~~~~~~~~~~~~~
// Type 'T[number][]' is not assignable to type 'T'.
// T[number][]' is assignable to the constraint of type 'T',
// but 'T' could be instantiated with a different subtype of constraint 'any[]'
}
在這里,我在to be的呼叫簽名中Array.prototype.reduce手動指定了型別引數T,以便編譯器知道將空陣列初始值[]和回呼prev和回傳型別都視為T。但是,如您所見,它抱怨T[number][]不能分配給T. 這是什么意思?
好吧,編譯器知道這[...prev, ...cur]將是一個新陣列,其元素與 的陣列元素相同T。的陣列元素T是您索引到具有 typeT數字索引的 type陣列時得到的number。所以,T[number]是該元素的型別。如果你制作一個陣列,它就是一個Array<T[number]>,又名T[number][]。
編譯器告訴你,它不能保證[...prev, ...cur]型別T[number][]也是型別T。特別是在T有額外屬性的情況下,那些屬性cur不會被復制到新陣列中,所以[...prev, ...cur]不會有這些屬性。我們可以驗證:
const arr1 = Object.assign([1, 2, 3], { a: 1 });
const arr2 = Object.assign([4, 5, 6], { a: 2 });
const flattenedBad = flattenArrayBad([arr1, arr2]);
/* const flattenedBad: number[] & {
a: number;
} */
try {
console.log(flattenedBad.a.toFixed(2)); // no compiler error, but
} catch (e) {
console.log(e); // RUNTIME ??! flattenedBad.a is undefined
}
這flattenedBad是由flattenArrayBad聲稱回傳 type 值的呼叫產生的T。在這種情況下T是 type number[] & {a: number},因為arr1和arr2都有一個額外a的 type 屬性number。的回傳型別注解flattenArrayBad就是T,所以flattenedBad在這方面也應該是型別number[] & {a: number}。這意味著編譯器允許我們使用鍵對其進行索引a并嘗試呼叫number類似的方法toFixed()。和kaboom,我們有一個運行時錯誤,因為回傳的實際值flattenArrayBad沒有額外的屬性。
這就是錯誤告訴你的;是的,T[number][]是 的子型別any[],但它可能不是 的子型別T,因為有人可以指定T與 不同的東西T[number][]。
如果您想保留T extends any[](而不是在陣列元素型別而不是陣列型別本身中使函式通用),這里的修復方法是讓flattenArray()回傳一個 type 的值,T[number][]編譯器已經看到reduce()回傳的型別:
function flattenArrayGood<T extends any[]>(a: T[]) {
return a.reduce<T[number][]>((prev, curr) => [...prev, ...curr], []);
}
const flattenedGood = flattenArrayGood([arr1, arr2]);
// const flattenedGood: number[]
flattenedGood.a // <-- compiler error, Property 'a' does not exist on type 'number[]'
現在flattenedGood被視為 typenumber[]并且不會被錯誤地認為具有 type 的屬性a。如果你試圖用 索引它a,編譯器就會生你的氣。所以你不能在flattenedGood.a.toFixed(2)沒有得到有用的編譯器錯誤的情況下呼叫。
Playground 鏈接到代碼
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/368256.html
上一篇:當兩種型別定義了相同的鍵但值的型別不同時,為什么我不能在另一種型別上使用一種型別的keyof
下一篇:在TypeScript中使用`useContext`-輸入“MyType”null`不可分配給型別“MyType”
