【问题标题】:Using the keys of an object literal as a Typescript type?使用对象文字的键作为 Typescript 类型?
【发布时间】:2019-10-12 05:00:49
【问题描述】:

我有一个对象,其中包含一些用于我的应用程序的预定义数据,这些数据存储在 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];
    //...
}

这感觉像是一个愚蠢的 hack,而且每当我必须进行更改时都非常不方便,因为我必须记住将这个语句放在正确的位置,运行程序,然后手动将值复制回源代码代码(或者实际上,只需自己输入更改即可)。


我愿意接受更好的解决方案。是否有提供我正在寻找的功能的语言功能?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    让 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.freeze 的对象初始值设定项的类型,键为somelonglist 等。Object.freeze 定义为@ 987654333@¹,因此它返回相同推断类型的 Readonly 版本。
    2. keyof 获取类型的键。
    3. 在这种情况下,typeof 是 TypeScript 的 typeof,而不是 JavaScript。在 TypeScript 中,有些地方需要运行时值,而其他地方需要编译时类型。 keyof 的操作数是期望编译时类型的地方,因此 typeof data 返回 data 的编译时类型,这是从 freeze 推断类型的只读版本(带有键somelonglist 等)。 (如果你有x = typeof data,那将是typeof,在需要运行时值的上下文中,所以它将是JavaScript 的typeof,而x 将获得运行时值"object"。)

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

    【讨论】:

    • @Shinigami - 在过去的几个月里,我只是非常随意地学习 TypeScript,并且经常被它处理事物的先进程度所震撼。 :-)
    • 看起来这个解决方案与对象本身的类型声明不兼容。假设我将一个对象声明为const numbers:{[key:string]:number} ={"one":1,"two":2};。然后keyof typeof numbers 只给出string|number。看起来 [key:string] 导致了问题。有没有办法解决这个问题?
    • @MinetteNapkatti - 是的,如果你提供一个明确的类型声明,那将会获胜,TypeScript 不会推断(除非确保你分配的内容与类型兼容)。我不明白该评论中的问题。 “解决方法”不是那样做。如果您希望 numbers 仅具有键 onetwo,请使用上述方法(让 TypeScript 推断类型)。如果您希望数字允许 any 字符串键,请使用您的 {[key:string]:number} 类型。
    • @T.J.Crowder 我的错,看起来我让原始示例过于笼统,并且歪曲了我的实际用例。幸运的是,我发现了一个关于这个确切问题的问题:stackoverflow.com/questions/54598322/…
    • @MinetteNapkatti - 很高兴你找到了。由于这是对实际发布的问题的正确答案,我认为可以接受答案。请注意, 认为这很重要。 :-)
    猜你喜欢
    • 2020-03-25
    • 1970-01-01
    • 2012-11-17
    • 1970-01-01
    • 1970-01-01
    • 2021-05-21
    • 2022-01-18
    • 2020-01-17
    • 1970-01-01
    相关资源
    最近更新 更多