TypeScriptでプロパティの最大数を使用して動的キーを入力します

受付中 プログラミング
2024-12-24
masato
タイプスクリプトでオブジェクトの動的プロパティの最大数を入力できるかどうか疑問に思いました。 したがって、基本的な例はイベントを追跡するためのものです。
        events.track('SOME_EVENT', { first: 'a', other: 'b', some: 'c'})
        
イベントデータは、それぞれの値で最大3つのプロパティを保持することになっているため、キーも動的である可能性があります。 基本的なもので入力しましたRecordが、許可されるプロパティの数に制限はありません。
        export interface Events {
          track: (name: string, params?: Record<string, string | number | unknown>) => void;
        }
        
これは可能ですか?
回答一覧
パラメータを使用して、関数内のオブジェクトにそれらを組み合わせることができますか?たぶんキー名は重要ですか?
masato
申し訳ありませんが、キー名は重要ではありません
masato
1〜3個(ただし1〜3個のみ)の不明な文字列名のプロパティを使用してオブジェクトインターフェイスを宣言する方法を考えることはできません(ただし、1つがないという意味ではありません。私はジャーニーマンレベルにすぎません。 TypeScriptを使用)。 タプルの結合に傾倒します
        type EventParam = [name: string, value: string | number | unknown];
        type EventParams = 
              [EventParam]
            | [EventParam, EventParam]
            | [EventParam, EventParam, EventParam];
        export interface Events {
            track: (name: string, params?: EventParams) => void;
        }
        
        declare let events: Events;
        // Works with 1:
        events.track("something", [["first", "a"]]);
        // Works with 2:
        events.track("something", [["first", "a"], ["other", "b"]]);
        // Works with 3:
        events.track("something", [["first", "a"], ["other", "b"], ["some", "c"]]);
        // Fails with 4:
        events.track("something", [["first", "a"], ["other", "b"], ["some", "c"], ["fourth", 42]]);
        // Error as desired −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^^^
        
しかし、それは最高の人間工学を持っていないかもしれません。
masato
おい !返信ありがとうございます。これはインターフェースの良い代替手段のように見えますが、配列の配列でオブジェクトを非表示にしているようです。
masato
私はここTuplifyUnionから使用して解決策を得ました: これがどれほど「安全」かわかりません(免責事項を参照)。TuplifyUnion順序はいつでも変更される可能性があるため、使用は安全でないと見なされます。ここでは順序は重要ではなく、この場合は要素の量だけが重要なので、ここで使用しても問題ないと思います。 このソリューションでは、0〜3個のキーを使用できます。他の金額が必要な場合は、それらをユニオンに追加するだけです(たとえば、1 | 21つまたは2つのキーを受け入れます)。
masato
        type UnionToIntersection<U> =
          (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never
        type LastOf<T> =
          UnionToIntersection<T extends any ? () => T : never> extends () => (infer R) ? R : never
        
        type Push<T extends any[], V> = [...T, V];
        
        type TuplifyUnion<T, L = LastOf<T>, N = [T] extends [never] ? true : false> =
          true extends N ? [] : Push<TuplifyUnion<Exclude<T, L>>, L>
        
        
        type MaxThreeProperties<T extends Record<string, any>> =
          TuplifyUnion<keyof T>["length"] extends 0 | 1 | 2 | 3 ? T : never
        //    add all acceptable key amounts here ^   ^   ^   ^
        
        function track<T extends Record<string, any>>(
          name: string, 
          params: MaxThreeProperties<T>
        ) {}
        
基本的に、すべてのキーをタプルに入れてから、タプルの長さを「手動で」チェックします。これは醜くなるかもしれませんが、他の量のプロパティに簡単に拡張できます。 ただし、欠点の1つは、エラーメッセージです。 タイプ'string'はタイプ'never'に割り当てることができません。(2322) これは、関数を使用している人にとって混乱を招く可能性があります... ここにいくつかのテストがあります:
        track('SOME_EVENT', {})                                  // works
        track('SOME_EVENT', {a: ""})                             // works
        track('SOME_EVENT', {a: "", b: ""})                      // works
        track('SOME_EVENT', {a: "", b: "", c: ""})               // works
        track('SOME_EVENT', {a: "", b: "", c: "", d: ""})        // ERROR
        track('SOME_EVENT', {a: "", b: "", c: "", d: "", e: ""}) // ERROR
        
        const a = {a: "", b: "", c: ""}
        const b = {a: "", b: "", c: "", d: ""}
        
        track('SOME_EVENT', a)  // works
        track('SOME_EVENT', b)  // ERROR
        
        
masato
これはうまくいくように見えますが、将来的に制限を増やす必要がある場合は、このタイプの定型文はさらに多くなります。どうもありがとう
masato
extends句の長いチェーンを次のように減らすことができますTuplifyUnion<keyof T>["length"] extends 0 | 1 | 2 | 3:)
masato
すごい短いです:Dはこれが物であることを知りませんでした
masato