![](/img/trans.png)
[英]How do I assign to a variable-specified key of a `Partial<MyInterface>` without an error that it can't be assigned to undefined?
[英]Why can't I assign to a variable-specified key of a `Partial<MyInterface>`?
在這個 TypeScript 代碼中,同樣在下面重復,有一個 function 它在應用程序的類型系統中從組件部分構建了部分 object。 但是,它不適用於注意到的錯誤,即所有結果的屬性都undefined
,從而阻止將任何其他內容分配給它們。 (然而,在所示的 hover 測試中,它們並未undefined
。)
我嘗試使用此處提到的KeyForValue
策略,但由於使用interface
而不是type
時原來的問題而失敗。 刪除對Record
實用程序類型的依賴刪除了該錯誤,但留下了一個關於 KeyForValue 類型如何比從常量數組中挑選的“一個”字符串更嚴格的問題。
我還嘗試將此策略與可變參數元組類型一起使用,該類型也不起作用,因為(readonly [...K])[0]
不能用於索引 K 是keyof
的類型。
該問題中列出的設計限制是否有另一種解決方法,以消除此錯誤,但仍能夠動態構建 object,其中鍵和相應值作為單獨的參數傳入?
Playground 鏈接上的代碼是:
interface HornPlayer {instrumentName: 'saxophone' | 'clarinet' | 'trumpet';}
interface ChordPlayer {instrumentName: 'piano' | 'organ' | 'vibraphone';}
interface BassPlayer {instrumentName: 'double bass' | 'tuba' | 'bass guitar';}
interface DrumPlayer {kitItemCount: number;}
type Instrumentalist = HornPlayer | ChordPlayer | BassPlayer | DrumPlayer;
interface JazzQuartet {
horn: HornPlayer,
chords: ChordPlayer,
bass: BassPlayer,
drums: DrumPlayer
}
const demoFn = function (
instrumentalists : [HornPlayer, ChordPlayer, BassPlayer, DrumPlayer],
) : Partial<JazzQuartet> {
//This array is hard-coded only to make the example simpler:
const roleNamesInSameOrder = ['horn', 'chords', 'bass', 'drums'] as (keyof JazzQuartet)[];
let result : Partial<JazzQuartet> = {};
const roleNameOutsideLoop = roleNamesInSameOrder[0]; // 'horn'
//Error ts(2322): Type 'HornPlayer' is not assignable to type 'undefined':
result[roleNameOutsideLoop] = instrumentalists[0];
result['horn'] = instrumentalists[0]; //this works fine though
for (let index in roleNamesInSameOrder) {
const roleName = roleNamesInSameOrder[index];
const instrumentalist = instrumentalists[index] as (JazzQuartet[typeof roleName]);
let test = result['horn']; //HornPlayer | undefined as expected
let test2 = result[roleName]; //HornPlayer | ChordPlayer | BassPlayer | DrumPlayer | undefined as expected
//Error ts(2322): Type 'HornPlayer' is not assignable to type 'undefined':
result[roleName] = instrumentalist;
}
return result;
}
這個類型:
const roleNamesInSameOrder = ['horn', 'chords', 'bass', 'drums'] as (keyof JazzQuartet)[];
不安全,因為您可以重復drums
兩次甚至三次:
const roleNamesInSameOrder = ['drums', 'drums', 'drums'] as (keyof JazzQuartet)[];
您將看到沒有 TS 錯誤。 還請記住:
const roleNameOutsideLoop = roleNamesInSameOrder[0]; // 'horn'
roleNameOutsideLoop
被推斷為keyof JazzQuartet
的 key 而不是horn
。
為了使這條線更安全,您需要使用TupleUnion
實用程序類型。 在這里,您可以在我的文章中找到更多示例。
interface HornPlayer { instrumentName: 'saxophone' | 'clarinet' | 'trumpet'; }
interface ChordPlayer { instrumentName: 'piano' | 'organ' | 'vibraphone'; }
interface BassPlayer { instrumentName: 'double bass' | 'tuba' | 'bass guitar'; }
interface DrumPlayer { kitItemCount: number; }
type Instrumentalist = HornPlayer | ChordPlayer | BassPlayer | DrumPlayer;
interface JazzQuartet {
horn: HornPlayer,
chords: ChordPlayer,
bass: BassPlayer,
drums: DrumPlayer
}
// stolen from here https://github.com/microsoft/TypeScript/issues/13298#issuecomment-692864087
type TupleUnion<U extends string, R extends any[] = []> = {
[S in U]: Exclude<U, S> extends never ? [...R, S] : TupleUnion<Exclude<U, S>, [...R, S]>;
}[U];
const demoFn = function (
instrumentalists: [HornPlayer, ChordPlayer, BassPlayer, DrumPlayer],
): Partial<JazzQuartet> {
//This array is hard-coded only to make the example simpler:
const roleNamesInSameOrder: TupleUnion<keyof JazzQuartet> = ['horn', 'chords', 'bass', 'drums'];
let result: Partial<JazzQuartet> = {};
const roleNameOutsideLoop = roleNamesInSameOrder[0]; // 'horn'
result[roleNameOutsideLoop] = instrumentalists[0]; // ok
result['horn'] = instrumentalists[0]; // ok
for (let index in roleNamesInSameOrder) {
const roleName = roleNamesInSameOrder[index];
const instrumentalist = instrumentalists[index] as (JazzQuartet[typeof roleName]);
let test = result['horn'];
let test2 = result[roleName];
result[roleName] = instrumentalist; // error
}
return result;
}
但是,我們這里仍然有一個錯誤result[roleName] = instrumentalist
。 我知道您有點困惑,因為此代碼按預期工作:
result[roleNameOutsideLoop] = instrumentalists[0]; // ok
這可以正常工作,因為 TS 能夠推斷出roleNameOutsideLoop
是horn
和instrumentalists[0]
是HornPlayer
。
但是,這里:
result[roleName] = instrumentalist; // error
roleName
是"horn" | "chords" | "bass" | "drums"
"horn" | "chords" | "bass" | "drums"
"horn" | "chords" | "bass" | "drums"
和instrumentalist
是HornPlayer | ChordPlayer | BassPlayer | DrumPlayer
HornPlayer | ChordPlayer | BassPlayer | DrumPlayer
HornPlayer | ChordPlayer | BassPlayer | DrumPlayer
。
這意味着從類型的角度來看,下一個案例可以占有一席之地:
// pseudo code
result['horn'] = BassPlayer
TS 無法在for..in loop
中跟蹤index
。 TS 不夠聰明,無法弄清楚index
、 roleNamesInSameOrder
和instrumentalists
主義者之間存在關系。 我的意思是,每次使用roleNamesInSameOrder[index]
時,都會得到一個聯合,而不是只有一種類型。
概括
通常,在 TypeScript 中,當您遇到此類問題時,您需要使用Array.prototype.reduce
:
interface HornPlayer { instrumentName: 'saxophone' | 'clarinet' | 'trumpet'; }
interface ChordPlayer { instrumentName: 'piano' | 'organ' | 'vibraphone'; }
interface BassPlayer { instrumentName: 'double bass' | 'tuba' | 'bass guitar'; }
interface DrumPlayer { kitItemCount: number; }
type Instrumentalist = HornPlayer | ChordPlayer | BassPlayer | DrumPlayer;
interface JazzQuartet {
horn: HornPlayer,
chords: ChordPlayer,
bass: BassPlayer,
drums: DrumPlayer
}
// stolen from here https://github.com/microsoft/TypeScript/issues/13298#issuecomment-692864087
type TupleUnion<U extends string, R extends any[] = []> = {
[S in U]: Exclude<U, S> extends never ? [...R, S] : TupleUnion<Exclude<U, S>, [...R, S]>;
}[U];
const ROLE_NAMES: TupleUnion<keyof JazzQuartet> = ['horn', 'chords', 'bass', 'drums'];
const fn = (
instrumentalists: [HornPlayer, ChordPlayer, BassPlayer, DrumPlayer],
): Partial<JazzQuartet> =>
ROLE_NAMES.reduce((acc, elem, index) => ({
...acc,
[elem]: instrumentalists[index]
}), {} as Partial<JazzQuartet>)
PS TypeScript 不喜歡突變
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.