【问题标题】:Is it possible to strongly type an object's keys based on array values?是否可以根据数组值强类型化对象的键?
【发布时间】:2020-12-28 18:45:39
【问题描述】:

为了使用标准化数据,我构造了一个对象,其键只能是给定数组中的数字列表。是否有某种方法可以在键入时反映这一点,这样,如果我尝试使用非数组值键对对象进行索引,我会得到一个错误(除了这个值可能未定义的错误)。

换句话说,对于这个 Foo 类型的对象:

 const x:Foo = {                                                 
   allIds: [1,3],                                                                  
   structured: {                                                                    
     1: "foo",          
     3: "bar"           
    } 
 }                       

是否有可能构造 Foo 导致以下错误,因为 2 不在数组 allIds 中?

const z = x.structured[2]

我已经尝试过了,但它似乎并没有起到作用,因为索引签名扩大到“数字”:

 interface Foo {                                                    
   allIds: number[]                                                               
   structured: Record<Foo["allIds"][number],string>                   
 }    

非常感谢任何帮助。

Playground

澄清:假设 allIds 的值在代码中是静态设置的,即在运行时不变化。

【问题讨论】:

  • allIds 中的值是否在运行时确定?它们在运行时会有所不同吗?如果是这样,不,您不能使用类型系统执行此操作。 (您可以在运行时使用代理进行。)
  • 好点;我已经澄清了这个问题。出于好奇,@t-j-crowder,你能告诉我你在运行时使用代理的意思吗?
  • 使用Proxy 对象,您可以拥有set 处理程序(如果相关,还可以使用get 处理程序)并拒绝尝试设置(或获取)具有非数字名称的属性在allIds.
  • (FWIW,我进入Proxy——以及相关的Reflect——在我新书的第14章中进行了深入探讨,如果你有兴趣,请在我的个人资料中详细介绍。但那对您在上面尝试执行的强类型没有帮助。)
  • @sam256 您的问题得到解答了吗?如果是,请接受,这样这个问题就可以结束了。

标签: typescript


【解决方案1】:

您可以做的不是使用接口Foo 输入x,而是使用const assertions。这将防止 allIds 的自动类型扩展为 number[] 而不是 readonly [1,3]

 const x = {                                                 
   allIds: [1,3] as const,                                                                  
   structured: {                                                                    
     1: "foo",          
     3: "bar"           
    } 
 } 

我假设您的对象x 无论如何都分为两部分,一部分是静态定义的allIds,另一部分可能是动态部分structured。然后,我们可以通过泛型参数 AllowedIndices 扩展您的接口 Foo,并为其提供我们声明为 const 的 allIds 类型。

const allIds = [1,3,4] as const;

 interface Foo<AllowedIndices extends readonly number[]> {                                                    
   structured: Partial<Record<AllowedIndices[number], string>>                
 }                                                                                  
                                                                                    
 const x: Foo<typeof allIds> = {                                                 
   structured: {                                                                    
     1: "foo",          
     3: "bar"           
    } 
 }                  

const z = x.structured[2] // error   

Link to Playground

示例说明:

我添加了另一个索引4 来举例说明Partial&lt;T&gt; 的使用:allIds 中的索引不一定需要在x.structured 中作为键出现。

【讨论】:

  • 那个。是。不错。
猜你喜欢
  • 2018-01-27
  • 2018-09-06
  • 1970-01-01
  • 1970-01-01
  • 2022-01-05
  • 2021-10-19
  • 2018-08-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多