使用对象文字的键作为Typescript类型?

时间:2019-05-26 09:10:34

标签: typescript

我有一个对象,其中包含我的应用程序的一些预定义数据,这些数据存储在这样的const变量中:

const data:{[key:string]:any} =Object.freeze({
    some: 123,
    long: {"a":"b"},
    list: ["c"],
    of: "",
    arbitrary: null,
    things: 1.2,
});

该对象的键对于应用程序的其余部分是已知的。考虑以下访问数据对象的函数:

function doWork(k) {
    if(!data.hasOwnProperty(k)) throw Error();
    let value = data[k];
    //...
}

这用类似的字符串来调用

doWork("things");

我想用Typescript编译时检查替换无效键的运行时错误。我希望能够写

function doWork(k: keyof data) {
    let value = data[k];
    //...
}

但是显然keyof运算符不能那样工作。我收到错误消息TS2304: Cannot find name 'data'.


我的解决方法: 我可以用以下方法提取对象的键:

console.log("\""+Object.keys(data).join("\"|\"")+"\"");

然后我可以复制/粘贴并定义为类型。

type data_key = "some"|"long"|"list"|"of"|"arbitrary"|"things"
export function doWork(k:data_key) {
    let value = data[k];
    //...
}

这感觉很愚蠢,每当需要进行更改时,都会感到非常不便,因为我必须记住在正确的位置放置此语句,运行程序,然后手动将值复制回源代码中代码(或者实际上,只需自己输入更改)。


我愿意寻求更好的解决方案。是否有语言功能可以提供我要寻找的功能?

1 个答案:

答案 0 :(得分:8)

让TypeScript推断data的类型,然后使用type data_key = keyof typeof data;从其推断的类型中提取键:

const data = Object.freeze({
    some: 123,
    long: {"a":"b"},
    list: ["c"],
    of: "",
    arbitrary: null,
    things: 1.2,
});

type data_key = keyof typeof data;

function doWork(k: data_key) {
    let value = data[k];
    //...
}

On the playground.

工作原理:

  1. TypeScript具有高级type inference,因此可以使用键Object.freezesomelong来推断传入list的对象初始化程序的类型。 ,等等。Object.freeze被定义为freeze<T>(o: T): Readonly<T>¹,因此它返回相同推断类型的Readonly版本。
  2. keyof获取类型的键。
  3. 在这种情况下,
  4. typeof是TypeScript的typeof,而不是JavaScript的。在TypeScript中,有一些地方需要运行时值,而其他地方则需要编译时类型。 keyof的操作数是需要编译时类型的地方,因此typeof data返回data的编译时类型,它是从{推断出的类型的只读版本{1}}(使用键freezesomelong等)。 (如果您有list,则在期望运行时值的上下文中为x = typeof data,因此它将是JavaScript的typeof,而typeof将获得运行时值x。)

¹lib.es5.d.ts中实际上有"object"的三个定义(一个用于数组,一个用于函数,以及一个用于所有其他类型的对象;这是最后一个)。