相互排他タイプ


間の違いtypeinterface タイプスクリプトのsは、彼らが非常に類似しているので、必ずしも明らかでありません.しかし、微妙な違いがあります.この記事では、型だけで実現できる機能について説明します.
まず始めましょう.
type Resource<Type = any> = {
    progress: 'pending' | 'success' | 'error'
    error?: Error
    data?: Type
}
このような構造は以下のように使われる:
  • セットprogress to "pending" リソースへのアクセス
  • セットprogress to "success"data 成功の場合
  • またはセットprogress to "error" とエラーerror 失敗した場合
  • しかし、これはどんな形でも強制されません.ハードコードされた値を書きましょう.
    // this one is a possible expected value :
    const res: Resource = {
        progress: 'pending'
    }
    
    // but this one is unexpected, however I can write it !
    const res: Resource = {
        progress: 'pending',
        data: 42             // 😵 oh no ! this is allowed
    }
    
    // this one doesn't make sense either !
    const res: Resource = {
        progress: 'pending',
        data: 42,            // 😵
        error: new Error()   // 😵
    }
    

    ユニオンタイプ


    タイプスクリプトの型の1つの強さは、型を結合する可能性を持つことです.つの相互排他タイプの結合として型を書き直しましょう.
    type Resource<Type = any> = {
        progress: 'pending'
    } | {
        progress: 'success'
        data: Type
    } | {
        progress: 'error'
        error: Error
    }
    
    そして、それはうまくいきます!さあ書きましょう
    // I can't write it anymore ! There is an error !
    const res: Resource = {
        progress: 'pending',
        data: 42             // 👈 yes ! there is an error
    }
    
    // the fix :
    const res: Resource = {
        progress: 'success', // 👌
        data: 42
    }
    
    今までのところ、これまでに、タイプスクリプトは現在、予想される方法の誤りを防ぎます.また、次のコードを書くのを防ぎますdata のフィールドではないres :
    const res: Resource = getSomeResource();
    doSomething(res.data); // 👈 error !
    
    
    データにアクセスするには、型ガードを使用して、別の種類のコードで実際の型を再構築することができます.
    const res: Resource = getSomeResource();
    if (res.progress === 'success') {
        doSomething(res.data); // 👌
    }
    
    タイプガードは確かにタイプスクリプトの中で最も貴重な機能の一つですが、残念ながら、これも書きません.
    const res: Resource = getSomeResource();
    const data = res.data ?? 42; // 😖 oh no ! error !
    
    なぜ?もう一度言いましょうdata のフィールドではないres !

    相互排他型権利


    それで、我々のタイプを固定しましょうdata 任意の場合に定義されるフィールド
    type Resource<Type = any> = {
        progress: 'pending'
        data?: never
        error?: never
    } | {
        progress: 'success'
        data: Type
        error?: never
    } | {
        progress: 'error'
        data?: never
        error: Error
    }
    
    そのようにdata フィールドは結果のunion型の無条件部分です変更は時々設定できません.
    // I still can't write that !
    const res: Resource = {
        progress: 'pending',
        data: 42             // 👈 yes ! there is an error !
    }
    
    ...しかし、我々がアクセスすることができるすべての回:
    // but now, I can write that :
    const res: Resource = getSomeResource();
    const data = res.data ?? 42; // 👌 got it !
    
    ...そしてもちろんタイプガードはまだ期待された方法で働いています!
    その結果、最後のタイプ定義は少し冗長です、しかし、コードはより少なくなります!したがって、特定の状況でのみこのテクニックを適用する価値があります.
    読んでくださってありがとうございます.