设置 Key/Value 的函数,其中键是文字(而不仅仅是字符串)
Function which sets a Key/Value where key is a literal (rather than just a string)
我希望我的简单 KV
函数生成一个对象,其中 key 是文字类型,而不是扩展为 string:
function KV<K extends Readonly<string>, V extends any>(key: K, value: V) {
return { [key as K]: value };
}
type Target = {foo: number};
const t: Target = KV("foo", 43);
type Target2 = { bar: (bar: string) => {bar: string}};
const t2: Target2 = KV("bar", (bar: string) => ({ bar }));
不幸的是,虽然通用 K
被 定义为文字,但我找不到强制 returned 键值的方法配对以将密钥识别为文字。
我觉得要让它工作我需要更明确地了解 return 类型所以我尝试了 KV 函数,如下所示:
function KV2<O extends object, K extends Readonly<string> & keyof O, V extends any, KV extends O & [K: V]>(key: K, value: V): KV {
return { [key as K]: value } as KV;
}
我的希望很高,但不幸的是没有培根。
此后我尝试了更多的变体,但没有得到它的工作。任何更精通文字类型的黑暗艺术的人有什么想法吗?
Code can be found in this Playground
好的,我想我已经回答了我的问题,所以我会 post 在这里回答我的问题,但如果有更好的解决方案,我愿意接受,并会在关闭前再开放一天:
解法:
export function KV<
K extends string,
V extends any,
O extends Record<K, V>
>(key: K, value: V): O {
return { [key as K]: value } as O;
}
这样我就可以组合键值,当我将它们合并在一起时,完整的类型将被保留,因为键是文字类型。所以在这个例子中:
const kv = KV("foo", 43);
const kv2 = KV("bar", (bar: string) => ({ bar }));
const both = { ...kv, ...kv2 };
类型both
保留了kv
和kv2
的所有类型信息。
根据@jcalz 的有用反馈,我从两个方面调整了我的解决方案。首先,上面的实现已简化为:
export function KV<V extends any, K extends string>(key: K, value: V) {
return { [key]: value } as Record<K, V>;
}
这将推断出 key 和 value 并且是一个很好的紧凑签名。但是,如果您想将 value 限制为更窄的定义,这将不起作用,因为这将需要声明两个泛型。这可以通过将构造与函数分开来避免,因此我为这个用例引入了 KV2
:
export const KV2 = <V extends any>() => <K extends string>(key: K, value: V) => {
return { [key]: value } as Record<K, V>;
};
这将允许您——例如——接受键值,其中值是必须以“kv-is-great”开头的字符串:
const kv =
KV2<`kv-is-great${string}`>()("foo", "kv-is-great-or-is-it?");
就其本身而言,这似乎不是很有用,但在更大的组合中能够约束 value 应该是一个有用的补充,但代价是稍微有点尴尬 API.
我希望我的简单 KV
函数生成一个对象,其中 key 是文字类型,而不是扩展为 string:
function KV<K extends Readonly<string>, V extends any>(key: K, value: V) {
return { [key as K]: value };
}
type Target = {foo: number};
const t: Target = KV("foo", 43);
type Target2 = { bar: (bar: string) => {bar: string}};
const t2: Target2 = KV("bar", (bar: string) => ({ bar }));
不幸的是,虽然通用 K
被 定义为文字,但我找不到强制 returned 键值的方法配对以将密钥识别为文字。
我觉得要让它工作我需要更明确地了解 return 类型所以我尝试了 KV 函数,如下所示:
function KV2<O extends object, K extends Readonly<string> & keyof O, V extends any, KV extends O & [K: V]>(key: K, value: V): KV {
return { [key as K]: value } as KV;
}
我的希望很高,但不幸的是没有培根。
此后我尝试了更多的变体,但没有得到它的工作。任何更精通文字类型的黑暗艺术的人有什么想法吗?
Code can be found in this Playground
好的,我想我已经回答了我的问题,所以我会 post 在这里回答我的问题,但如果有更好的解决方案,我愿意接受,并会在关闭前再开放一天:
解法:
export function KV<
K extends string,
V extends any,
O extends Record<K, V>
>(key: K, value: V): O {
return { [key as K]: value } as O;
}
这样我就可以组合键值,当我将它们合并在一起时,完整的类型将被保留,因为键是文字类型。所以在这个例子中:
const kv = KV("foo", 43);
const kv2 = KV("bar", (bar: string) => ({ bar }));
const both = { ...kv, ...kv2 };
类型both
保留了kv
和kv2
的所有类型信息。
根据@jcalz 的有用反馈,我从两个方面调整了我的解决方案。首先,上面的实现已简化为:
export function KV<V extends any, K extends string>(key: K, value: V) {
return { [key]: value } as Record<K, V>;
}
这将推断出 key 和 value 并且是一个很好的紧凑签名。但是,如果您想将 value 限制为更窄的定义,这将不起作用,因为这将需要声明两个泛型。这可以通过将构造与函数分开来避免,因此我为这个用例引入了 KV2
:
export const KV2 = <V extends any>() => <K extends string>(key: K, value: V) => {
return { [key]: value } as Record<K, V>;
};
这将允许您——例如——接受键值,其中值是必须以“kv-is-great”开头的字符串:
const kv =
KV2<`kv-is-great${string}`>()("foo", "kv-is-great-or-is-it?");
就其本身而言,这似乎不是很有用,但在更大的组合中能够约束 value 应该是一个有用的补充,但代价是稍微有点尴尬 API.