[英]Type dynamic keys with max number of properties in typescript
我想知道是否可以在打字稿中鍵入對象中動態屬性的最大數量。
所以基本的例子是跟蹤事件:
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;
}
這可能嗎?
我想不出一種方法來聲明一個具有 1-3 個(但只有 1-3 個)未知字符串命名屬性的對象接口(但這並不意味着沒有;我只是在熟練工級別使用打字稿)。
我傾向於元組的聯合:
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 −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^^^
但這可能沒有最好的人體工程學。
我從這里得到了使用TuplifyUnion
的解決方案:
我不確定這有多“安全”(請參閱免責聲明)。 使用TuplifyUnion
被認為是不安全的,因為順序可能隨時更改。 由於順序在這里並不重要,在這種情況下只有元素的數量很重要,我認為在這里使用它是可以的。
該解決方案允許 0-3 個密鑰。 如果您想要任何其他數量,只需將它們添加到聯合中(例如1 | 2
接受 1 或 2 個鍵)。
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>
) {}
我們基本上將所有鍵放入一個元組中,然后“手動”檢查元組的長度。 這可以很容易地擴展到其他數量的屬性,盡管它可能會變得丑陋。
一個缺點是錯誤消息:
類型“字符串”不可分配給類型“從不”。(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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.