為什么 TypeScript 接受seta何時不回傳型別物件的定義A?
type A = {
a: '123',
b: '456'
}
// Returns copy of obj with obj[k] = '933'
function seta<K extends keyof A>(k: K, obj: A): A {
return {
...obj,
[k]: '933'
}
}
const w: A = seta('a', {
a: '123',
b: '456'
})
// now w = {a: '933', b: '456'}
https://tsplay.dev/wEGX4m
uj5u.com熱心網友回復:
這看起來像是 TypeScript 中的錯誤或限制;請參閱microsoft/TypeScript#37103(標記為錯誤)或microsoft/TypeScript#32236(標記為“需要調查”)。
當您將屬性擴展到物件字面量然后添加計算屬性時,除非計算出的鍵是單個特定的字面型別(不是字面量的聯合,也不是泛型型別),否則 TypeScript 似乎會完全忽略計算屬性引數限制為字串文字):
function testing<K extends "a" | "b">(a: "a", x: string, y: "a" | "b", z: K) {
const someObject = { a: "v" } // { a: string }
const objA = { ...someObject, [a]: 0 } // { a: number } ??
const objX = { ...someObject, [x]: 0 } // { a: string } ??
const objY = { ...someObject, [y]: 0 } // { a: string } ??
const objZ = { ...someObject, [z]: 0 } // { a: string } ??
}
計算屬性鍵通常在 TypeScript 中有點問題,即使沒有傳播,因為它們往往會一直擴展到string,丟失您可能關心的資訊(請參閱microsoft/TypeScript#13948 上的另一個錯誤):
function testing2<K extends "a" | "b">(a: "a", x: string, y: "a" | "b", z: K) {
const objA = { [a]: 0 } // { a: number } ??
const objX = { [x]: 0 } // { [x: string]: number; } ???♂?
const objY = { [y]: 0 } // { [x: string]: number; } ???♂?
const objZ = { [z]: 0 } // { [x: string]: number; } ???♂?
}
所以這就是你在這里遇到的問題。
除了“輕輕地圍繞計算屬性”之外,我不知道該說什么。您可以通過定義自己的函式來解決它,該函式生成計算屬性的更“正確”版本:
function kv<K extends PropertyKey, V>(k: K, v: V) {
return { [k]: v } as { [P in K]: { [Q in P]: V } }[K]
}
const k = Math.random() < 0.5 ? "a" : "b";
const obj = kv(k, Math.random());
/* const obj: {
a: number;
} | {
b: number;
} */
您可以看到鍵的聯合產生了物件的聯合。但是,如果鍵是泛型的,那么編譯器會留下未評估的值,并且與泛型一起傳播往往會表示為一個交集,這通常是可以接受的,但當鍵重疊時就不行了。(有關該問題,請參閱microsoft/TypeScript#32022):
function setA2<K extends keyof A>(k: K, obj: A): A {
return {
...obj,
...kv(k, '933')
}
/* const ret1: A & { [P in K]: { [Q in P]: string; }; }[K] */ // no error
}
因此,在收到任何警告之前,您必須擴大k從K到keyof A:
function setA3<K extends keyof A>(k: K, obj: A): A {
const kW: keyof A = k;
return {
...obj,
...kv(kW, '933')
}; // FINALLY AN ERROR
// Type '{ a: string; b: "456"; } | { b: string; a: "123"; }' is not assignable to type 'A'.
}
所以,萬歲,我猜……你可以花很多精力來解決這個函式的某種型別的安全問題,但這是一場非常慘烈的勝利(我認為這是一個詞)。因此,再次注意計算屬性,直到并且除非相關的 GitHub 問題得到修復。
Playground 鏈接到代碼
uj5u.com熱心網友回復:
它被接受,因為如果你將滑鼠懸停在[k]: '933',它上面說
(引數) k: K 擴展keyof A
這意味著您要回傳從回傳型別擴展的屬性,這是允許的。
當你宣告一個物件必須是一個介面時,你是說它必須至少具有那些屬性,而不僅僅是那些屬性。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/382079.html
