在运行时从 TypeScript 中的属性中提取"defined"类型



我想做什么

我目前正在一个对象的键上循环,并将值传递给另一个对象。

interface From {
[key: string]: string;
}
let from: From = {
prop1: "foo",
prop2: "23",
};
interface To {
[key: string]: string | number | boolean | undefined;
prop1: string;
prop2: number;
prop3: boolean;
}
let to: To = {} as To;
const initEnv = async () => {
const keys: string[] = Object.keys(from);
for (let i = 0; i < keys.length; i++) {
let key: string = keys[i];
let keyType = typeof (key as keyof To); // This only returns "string". Which kind of makes sense to me
let keyType = typeof keyof key; // SyntaxError: ',' expected
let keyType: typeof to[key]; // Error: 'key' refers to a value, but is being used as a type
to[key] = from[key];
}
};

我希望能够,比如切换值,所以我不想只提取键的类型。我想将它分配给一个变量,以便在代码中使用,因此在运行时,例如作为字符串
所以我认为这样的事情行不通。

let keyType2: typeof env[key]; // Error: 'key' refers to a value, but is being used as a type

也许吧,但问题是;我该给这个变量分配什么?

这一切的原因是,我想在之前将from variables转换为正确的类型,并将它们分配给to对象。

所以,是的,基本上,我的问题是如何key中提取类型(作为字符串,在运行时动态地(。或者这在一开始是可能的吗?如果不是,为什么不呢?我喜欢理解事物。

也谢谢你忍受我糟糕的英语。

运行时没有interface;TypeScript的类型系统将从发出的JavaScript中删除。您的fromto值将在运行时进行如下评估:

let from = {
prop1: "foo",
prop2: "23",
};
let to = {};

没有FromTo,也没有办法使用To来找出如何将from的属性强制转换为正确的类型。类型系统没有运行时效果。


TypeScript的类型系统的有用性来自于描述运行时将发生的事情,而不是影响运行时的事情。想象一下,您必须如何用纯JavaScript编写代码,然后为代码的指定类型。这里有一种方法可以实现。让我们制作一个To对象,而不是To接口,它的属性是将输入强制转换为其他类型的函数:

const To = {
prop1: String,
prop2: Number,
prop3: Boolean
}

这些信息足以在运行时继续。


现在,如果您要手动构建to,编译器将能够理解生成的值具有类型为stringprop1属性和类型为number:的prop2属性

const toManual = { prop1: To.prop1(from.prop1), prop2: To.prop2(from.prop2) };
/* const toManual: { prop1: string; prop2: number; } */

但你不想手动操作;您想要编写一个循环,它遍历from的键并使用To生成to的属性。这对编译器来说更难理解,但通过明智地使用类型断言和类型注释,您可以编写一个以编程方式工作的objMap函数:

function objMap<T, F extends { [K in keyof T]: (arg: T[K]) => any }>(
obj: T, fMap: F) {
const ret = {} as { [K in keyof T]: ReturnType<F[K]> };
const fM: { [K in keyof T]: (arg: T[K]) => ReturnType<F[K]> } = fMap;
(Object.keys(obj) as Array<keyof T>).forEach(<K extends keyof T>(k: K) => {
ret[k] = fM[k](obj[k]);
})
return ret;
}

objMap函数采用对象obj和映射对象fMap,该映射对象至少具有与obj相同的所有密钥,并且其属性是映射obj的属性的函数。objMap的返回类型是一个对象,其属性是obj中每个属性的fMap函数的所有返回值。实际工作由ret[k] = fM[k](obj[k]);完成。它是ret.prop1 = To.prop1(from.prop1);ret.prop2 = To.prop2(from.prop2)的程序等价物。

让我们看看它是否有效:

const to = objMap(from, To);
/* const to: { prop1: string; prop2: number; } */
console.log(JSON.stringify(to));
// {"prop1":"foo","prop2":23}

这看起来是正确的;编译器推断to的类型为{prop1: string, prop2: number}并且在运行时计算to的实际值为{prop1: "foo", prop2: 23}


好的,希望能有所帮助;祝你好运

游乐场链接到代码

最新更新