【问题标题】:Type-checking the keys of a const literal without losing the type在不丢失类型的情况下对 const 字面量的键进行类型检查
【发布时间】:2020-08-12 18:47:59
【问题描述】:

我想要一个引用接口键的常量。 我希望对这个常量进行类型检查,以确保键名正确,并且我还希望将它们键入为文字。

如果我执行以下操作:

interface MyInterface {
  keyA: string;
  keyB: string;
}

export const MY_KEYS: Record<string, keyof MyInterface> = {
  CONSTANT_KEY_A: 'keyA',
  CONSTANT_KEY_B: 'keyB',
} as const;

MY_KEYS.CONSTANT_KEY_A

那么MY_KEYS.CONSTANT_KEY_A 的类型是'keyA' | 'keyB'。但我希望它是'keyA'

如果我删除Record&lt;string, keyof MyInterface&gt;,它会起作用,但是我的键不再被类型检查为在 MyInterface 的键中。

知道如何实现这一目标吗?

例如,我可以添加第二个死变量来执行记录检查,并仅使用 as const 保留我的第一个变量,而不是强制转换为记录,但它非常冗长且不是很清楚。

谢谢!

【问题讨论】:

    标签: typescript


    【解决方案1】:

    通常在这种情况下,我会引入一个辅助函数,它会检查一个值是否可以分配给一个类型,而不会将值扩大到该类型。辅助函数的一般版本如下所示:

    const checkType = <T>() => <U extends T>(u: U) => u;
    

    然后让它检查你调用它的特定类型并手动指定该类型:

    const checkKeys = checkType<Record<string, keyof MyInterface>>();
    

    现在你有一个函数,它只接受正确类型的值,当你做错事时会给你预期的错误:

    export const MY_KEYS = checkKeys({
      CONSTANT_KEY_A: 'keyA',
      CONSTANT_KEY_B: 'keyB',
      // CONSTANT_KEY_C: 'keyC', // error! '"keyC"' is not assignable to  '"keyA" | "keyB"'.
    })
    

    但函数的输出没有按需要加宽:

    MY_KEYS.CONSTANT_KEY_A // "keyA"
    

    请注意,您甚至不需要as const,因为编译器认为您希望将对象文字解释为可分配给Record&lt;string, keyof MyInterface&gt;,因此不会将文字字符串值扩大到string。不过,如果 as const 在其他方面对您有帮助,您仍然可以使用它。


    从概念上讲,这与您的“死变量”想法并没有什么不同,当然在运行时(() =&gt; u =&gt; u)()(someValue) 是写someValue 的一种奇怪方式。但是如果没有 TypeScript 中内置的“验证可分配性而不扩大”运算符(可能是microsoft/TypeScript#30809?),这是我通常知道的最佳方法。

    好的,希望对您有所帮助;祝你好运!

    Playground link to code

    【讨论】:

    • 非常感谢您的回答!但是不行?MY_KEYS.CONSTANT_KEY_A的类型是"keyA" | "keyB"...
    • 抱歉,"keyC" 示例导致了这种情况;该错误使编译器放弃了类型推断。我把它注释掉了,你会看到现在的值是"keyA" 类型。
    • 太棒了,它确实有效!非常感谢,它完全符合我的需求?
    猜你喜欢
    • 1970-01-01
    • 2023-02-10
    • 2010-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多