例如。鑒于以下代碼。我在最后一個 compat[k] 上收到打字稿錯誤,并顯示錯誤
型別“keyof T”不能用于索引型別“Partial<CompatType>”
export type KeysOfType<T, U, B = false> = {
[P in keyof T]: B extends true
? T[P] extends U
? U extends T[P]
? P
: never
: never
: T[P] extends U
? P
: never;
}[keyof T];
export type BigIntKeys<T> = KeysOfType<T, bigint, true>
export type CompatType<T> = Omit<T, BigIntKeys<T>> &
{
[Property in BigIntKeys<T>]: string;
};
export function compatModel<T>(model: T): CompatType<T> {
const compat: Partial<CompatType<T>> = {};
for (const k of Object.keys(model) as Array<keyof T>) {
const v = model[k];
compat[k] = typeof v === "bigint" ? v.toString() : v;
}
return compat as CompatType<T>;
};
型別應該在它們的鍵上完全重疊,但物件上的值的型別不同。這應該意味著我可以使用一個鍵來索引另一個,但它不會以這種方式出現。有什么我誤解或我錯了嗎?
TS游樂場
uj5u.com熱心網友回復:
這是 TypeScript 的設計限制;請參閱microsoft/TypeScript#28884。根據此評論,“使用Pick<T, K>或通過其他方式構造的高階型別的互補子集不能分配回該高階型別。”
所以像Omit<T, K> & Record<K, string>where這樣的型別K extends keyof T不會被視為與 具有相同的鍵T,即使它幾乎是必須的。當和/或是未指定的泛型型別時,編譯器比較Exclude<keyof T, K> | Extract<keyof T, K>并且keyof T不認為它們相等:TK
function foo<T, K extends keyof T>(a: keyof T) {
const b: Extract<keyof T, K> | Exclude<keyof T, K> = a; // error!
}
對于任何特定型別Tand K,編譯器可以完全評估Extract<keyof T, K> | Exclude<keyof T, K>并看到它與 相同keyof T,但是當T和/或K不是特定型別時,編譯器會推遲此評估,因此它不知道它們是否相同。
您可以做的一件事是直接從構建CompatType為同態映射型別T,并使用條件型別來決定特定鍵Kfromkeyof T是否屬于其中BigIntKeys<T>并相應地選擇值型別:
type CompatType<T> = { [K in keyof T]:
T[K] extends bigint ? bigint extends T[K] ? string : T[K] : T[K]
}
這會產生更好看的型別,
type Check = CompatType<{ a: string, b: bigint, c: number, d: boolean }>;
/* type Check = {
a: string;
b: string;
c: number;
d: boolean;
} */
并且編譯器知道它CompatType<T>肯定與 具有相同的鍵T,即使對于泛型T:
export function compatModel<T>(model: T): CompatType<T> {
const compat: Partial<CompatType<T>> = {};
for (const k of Object.keys(model) as Array<keyof T>) {
const v = model[k];
compat[k]; // no error here
compat[k] = typeof v === "bigint" ? v.toString() : v; // still error here, unrelated
}
return compat as CompatType<T>;
};
當然,在嘗試分配typeof v === "bigint" ? v.toString() : v給時仍然會出錯compat[k],但那是因為編譯器并不真正知道如何驗證某些內容是否可分配給條件型別(請參閱microsoft/TypeScript#33912),也不了解之間的相關性型別compat[k]時被寫入和型別typeof v === "bigint" ? v.toString() : v被讀取時(見微軟/打字稿#30581尤其是與事實寫入工會要求十字路口按照微軟/打字稿#30769)。這些問題超出了所問問題的范圍,這只是“為什么keyof不適合我”。
不管怎樣,在你知道在哪里的情況下你在做什么是正確的,但編譯器是不是,你可以使用型別斷言或該any型別放松型別檢查足以讓編譯器高興。例如:
export function compatModel<T>(model: T): CompatType<T> {
const compat: Partial<Record<keyof T, any>> = {};
for (const k of Object.keys(model) as Array<keyof T>) {
const v = model[k];
compat[k] = typeof v === "bigint" ? v.toString() : v;
}
return compat as CompatType<T>;
};
這里我們告訴編譯器不要擔心 的屬性值型別compat,我們只回傳它as CompatType<T>。只要您絕對 100% 確定打字正確,就可以這樣做。雖然,您可能不應該如此確定:
const hmm = compatModel({ a: Math.random() < 10 ? 3n : 3 });
hmm.a // number | bigint
if (typeof hmm.a !== "number") {
3n * hmm.a; // no error at compile time, but runtime ?? "can't convert BigInt to number"
}
a屬性的型別是number | bigint,根據 的任一定義CompatType<{a: number | bigint}>將變成{a: number | bigint}而不是正確的{a: number | string}。所以編譯器認為hmm.a可能是一個bigint即使這是不可能的。這里也有修復,但這些也超出了范圍,答案已經比我想要的要長。
這只是一個警告,提醒您在使用型別斷言或any克服編譯器錯誤時應格外小心,因為這樣的錯誤更有可能泄漏。
Playground 鏈接到代碼
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/368255.html
