我一直在尝试用Google搜索如何解决此问题,但即使要提出要查找的内容也很难,因此在编辑此问题或将我链接到重复项方面的任何帮助都将非常有帮助。
我有一个函数,作为第一个参数,将“列名”的记录作为键,将接口字段作为值。例如,
{
"user_id": "userID",
"email__": "email",
}
和一个接口,
interface ExpectedShape {
userID: number;
email: string;
}
由于我无法控制的原因,我上面的数据所在的列被命名为user_id
和email__
,并且将始终以字符串形式返回。因此,我需要定义在创建与ExpectedShape
接口匹配的JS对象时如何转换或清除传入的数据。
因此,为了允许这样做,我在我的方法中有一个配置变量,该变量将使用与原始映射相同的键,但是具有将相应地进行转换的方法。例如,
{
"user_id": Number.parseInt,
"email__": sanitizeEmail,
}
因此,当user_id
来自数据源时,它将进行调用,Number.parseInt
以便在ExpectedShape
构建对象时,它将是一个适当的数字。
通过TypeScript,我想强制使“映射”和“广播操作”的键相同。
下面是一些断码不起作用,但显示了我会想象它的样子。
interface ExpectedShape {
userID: number;
email: string;
}
interface FunctionOptions<K extends keyof any, S> {
casting: Record<K, (value: string) => S[keyof S]>;
}
function myFunction<T extends {}>(
values: Record<string, keyof T>,
config: FunctionOptions<keyof values, T>, // <-- What can I put here?
): Partial<T> {
return {};
}
function sanitizeEmail(_: string): string { return ""; }
const result = myFunction<ExpectedShape>(
{
"user_id": "userID",
"email__": "email",
},
{
// I want to restrict via TypeScript that the keys of `casting` must be
// the same as the keys of the `values` argument. See how it's missing
// an underscore?
casting: {
userid: Number.parseInt,
}
},
);
我已经尝试了很多。我最能成功工作并成功编译的是这个,
function myFunction<
T extends {},
M extends Record<string, keyof T> = Record<string, keyof T>,
P extends keyof M = keyof M,
>(
values: M,
config: FunctionOptions<P, T>,
): Partial<T> {
return {};
}
然而。它不能正确执行密钥,并且允许我使用所需的任何密钥。
我认为,为了使其在运行时或在类型系统上都能正常工作,您需要传递类似以下三个参数的内容:您要处理的输入对象;一个将输入对象的键映射到输出对象的键的对象;一个将输入对象的值映射到输出对象的值的对象。假定键映射器将只是其值为键的对象,而值映射器将是其值为功能的对象。
这是一种可能的实现:
function mapKeysAndValues<I extends object,
KM extends Record<keyof I, S>,
VM extends { [K in keyof I]: (v: I[K]) => any },
S extends PropertyKey
>(
inputObject: I,
keyMapping: KM,
valMapping: VM
): { [K in keyof I as KM[K]]: ReturnType<VM[K]> } {
const ret: any = {};
(Object.keys(inputObject) as Array<keyof I>).forEach(k =>
ret[keyMapping[k]] = valMapping[k](inputObject[k] as any)
)
return ret;
}
这里有三个重要的泛型类型参数:I
对应于输入对象,KM
对应于键映射对象和VM
对应于值映射对象。
(type参数S
除了提供提示我们希望编译器将键映射对象的值解释为文字值,而不是像其他没用的东西之外,并没有其他作用string
。有关功能请求,请参阅microsoft / TypeScript#30680进行讨论。您几乎可以忽略S
)。
繁重的工作是的返回类型mapKeysAndValues()
。我们在映射类型中使用键重新映射K
,将输入KM[K]
中的每个键替换为输出中的键。而我们使用的ReturnType<T>
工具类型的属性值抓住每个属性的返回类型K
的函数VM
,并用它作为值类型的输出类型的相应属性。
该实现使用一堆类型断言来防止编译器抱怨(它确实不理解键重映射或相关函数),但是在运行时,您可以看到它几乎只是遍历输入对象键并对每个键执行转换。 。(从技术上讲,inputObject
可能具有编译器未知的其他属性,因此您可能希望通过执行其他运行时检查(例如if (k in valMapping)
和)来使其更加安全if (typeof valMapping[k] === "function")
。但是,我将忽略这种可能性。
让我们测试一下:
const ret: ExpectedShape = mapKeysAndValues(
{ "user_id": "123", "email__": "[email protected]" },
{ "user_id": "userID", "email__": "email" },
{ "user_id": Number.parseInt, "email__": sanitizeEmail }
)
console.log(ret);
/* {
"userID": 123,
"email": "[email protected]"
} */
看起来不错!编译器知道mapKeysAndValues()
会产生该ExpectedShape
类型的值,并且当我们将其记录到控制台时,我们看到在运行时也发生了正确的事情。
希望您可以将其mapKeysAndValues()
用作尝试完成的转换的起点。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句