web-dev-qa-db-ja.com

クラス内の変数の値に基づく条件付きタイプ(TypeScript)

私はLoadable<T>という名前の汎用クラスを作成しています。 1つはstateという名前で、ロード可能リソースの現在の状態を含みます。 2番目のものはdataという名前で、ロード可能リソースの値が含まれています。

state"loading" | "loaded" | "error"として定義されています。dataの現在の値に基づいてstateを変更したいと思います。

たとえば、state"loading"の場合、datanullになります。これを行うために構文を把握することはできません。

次のように定義されているLoadableState<T, U>という名前のtypeを作成しました。

    type LoadableState<T, U> =
        T extends "loaded" ? U :
        T extends "loading" ? null :
        T extends "error" ? string :
        never;

Tが渡されたタイプ"loaded" | "loading" | "error"でなければなりません。

この部分は動作しますが、これからdataを定義しようとしていません。

export class Loadable<T> {
    public state: "loaded" | "loading" | "error" = "loading";

    public data: LoadableState<state, T>;
    //                         ^^^^^ Cannot find name 'state`
}

TypesScriptは次のエラーをスローします。Cannot find name 'state'


私が試した他のもの:

public data: LoadableState<this.state, T>;
// Duplicate identified 'state'

public data: LoadableState<typeof this.state, T>;
// Cannot find name 'this'

public data: LoadableState<typeof state, T>;
// Cannot find name 'state'

public data: state extends "loaded" ? T :
    state extends "loading" ? null :
    state extends "error" ? string :
    never;
// Cannot find name 'state'

これが実際に可能なかどうかはかなりありません。そうでなければ、この問題に対する別の解決策はありますか?

7
hdt80

あるクラスプロパティのタイプが別のプロパティの値に依存する方法があるとは思わない(または少なくともきれいな方法ではありません)。

これらのプロパティの両方を単一のオブジェクトに組み合わせることをお勧めします。

type LoadState<T> = {
    state: "loading",
} | {
    state: "loaded",
    data: T
} | {
    state: "error",
    data: string; // or maybe 'message'
}

export class Loadable<T> {
    state: LoadState<T> = { state: "loading" }
}
 _

クラスラッパーは実際には非常に便利ではありませんが、クラスをまったく使用しないことを検討するかもしれません。

このタイプを使用する場合、データにアクセスしようとする前に、TypeScriptはstateをチェックすることを確認します。

function handleLoadState<T>(loadState: LoadState<T>) {
   loadState.data // ERROR, can't access data, since it might not exist
   if(loadState.state === "loaded")
       loadState.data // okay
   }
}
 _

このパターンは 識別共用体 と呼ばれ、タイプスクリプトではかなり一般的に使用されています。

17
Retsam