給定以下迭代物件屬性的函式:
function iterate<O extends Record<string, unknown>>(o: O, cb: (key: keyof O, value: O[keyof O], obj: O) => void) {
for (const key in o) {
if (o.hasOwnProperty(key)) {
cb(key, o[key], o);
}
}
}
在以下代碼中,對物件的索引會縮小其型別,但檢查鍵名不會:
iterate({
name: "Sally",
age: 30,
}, (key, value, obj) => { // key: "name" | "age"; value: string | number
if (key === "name") {
const indexedValue = obj[key]; // indexedValue: string;
console.log(value.toUpperCase(), indexedValue.toUpperCase());
} else {
const indexedValue = obj[key]; // indexedValue: number;
console.log(value.toFixed(), indexedValue.toFixed());
}
});
結果,value.toUpperCase()是一個錯誤,因為它value可能不是一個字串,但indexedValue.toUpperCase()因為它被縮小了所以有效。同樣,value.toFixed()是一個錯誤,因為它value可能不是一個數字,但indexedValue.toFixed()因為它被縮小了所以有效。
有沒有辦法指定型別,iterate以便key回呼內部的條件比較可以縮小value?如果不是,為什么在檢查鍵時索引縮小型別而不是?它們看起來是等價的,但如果在某些情況下我沒有想到兩者不等價的地方,我不會感到驚訝,因此關鍵比較作為縮小價值的一種方式并不安全。
TS游樂場
uj5u.com熱心網友回復:
key您需要在和之間建立關系value。就目前而言,TS 無法證明"name"不會與number.
你可以做到這一點的方法是生成一個有區別的元組聯合。對于給定的示例,這將是["name", string] | ["age", number]. 然后將其用作回呼的引數。在最近的版本中,TypeScript(自 4.6 PR起)可以遵循從有區別的聯合中解構的變數在它們之間具有關系,并且value在您使用時很窄key。
我們可以使用映射型別來為型別中的每個鍵生成元組,然后索引到映射型別以獲得元組的聯合。
type KeyValueEntries<T> = {
[P in keyof T]: [key: P, value: T[P], obj: T]
}[keyof T]
function iterate<O extends Record<string, unknown>>(o: O, cb: (...a:KeyValueEntries<O>) => void) {
for (const key in o) {
if (o.hasOwnProperty(key)) {
cb(key, o[key], o);
}
}
}
iterate({
name: "Sally",
age: 30,
}, (key, value, obj) => {
if (key === "name") {
const indexedValue = obj[key];
console.log(value.toUpperCase(), indexedValue.toUpperCase());
} else {
const indexedValue = obj[key];
console.log(value.toFixed(), indexedValue.toFixed());
}
});
游樂場鏈接
uj5u.com熱心網友回復:
回呼方法中沒有足夠的資訊來說明 value 是 obj[key] 的型別。
假設 TypeScript 選擇了它obj[key]并value在此處具有相同的型別。這種轉換讓我們可以想象 TypeScript 正在這樣做:
if (key === "name") {
const indexedValue = obj[key];
console.log((value as string).toUpperCase(), indexedValue.toUpperCase());
} else {
const indexedValue = obj[key];
console.log((value as number).toFixed(), indexedValue.toFixed());
}
這是有問題的——如果你這樣做了,然后用這個方法換出迭代,一切仍然會在運行時編譯和崩潰:
function iterate<O extends Record<string, unknown>>(o: O, cb: (key: keyof O, value: O[keyof O], obj: O) => void) {
const keys: (keyof O)[] = Object.keys(o);
keys.forEach((key, i) => {
cb(key, o[keys[(i 1) % keys.length]], o);
})
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/514951.html
標籤:打字稿缩小
