簡體   English   中英

為什么我不能分配給“部分”的變量指定鍵<myinterface> `?</myinterface>

[英]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 能夠推斷出roleNameOutsideLoophorninstrumentalists[0]HornPlayer

但是,這里:

result[roleName] = instrumentalist; // error

roleName"horn" | "chords" | "bass" | "drums" "horn" | "chords" | "bass" | "drums" "horn" | "chords" | "bass" | "drums"instrumentalistHornPlayer | ChordPlayer | BassPlayer | DrumPlayer HornPlayer | ChordPlayer | BassPlayer | DrumPlayer HornPlayer | ChordPlayer | BassPlayer | DrumPlayer

這意味着從類型的角度來看,下一個案例可以占有一席之地:

// pseudo code
result['horn'] = BassPlayer

TS 無法在for..in loop中跟蹤index TS 不夠聰明,無法弄清楚indexroleNamesInSameOrderinstrumentalists主義者之間存在關系。 我的意思是,每次使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM