【问题标题】:Is it possible to have dynamic typing based on object properties?是否可以基于对象属性进行动态类型化?
【发布时间】:2020-01-20 23:20:32
【问题描述】:

我有以下结构:

type ErrorObject = {
  name: string,
  message: string,
}

const ALL_ERRORS: ErrorObject[] = [
   {
     name: 'No-empty-name',
     message: 'You must provide a name',
   },
   {
     name: 'No-empty-age',
     message: 'You must provide an age',
   },
   {
     name: 'No-under-18',
     message: 'You must be 18+ to continue',
   }
]

现在我正在尝试创建一个类型来定义所有可能的错误names,最终应该是这样的:

type PossibleErrorNames = 'No-empty-name' | 'No-empty-age' | 'No-under-18'

这可以正常工作,但在我的实际项目中,显然数组要大得多,更新这两种类型感觉非常缓慢,因为它们会相互依赖(如果你更新一个,你有更新另一个)。


我尝试了以下方法:

const ALL_NAMES = ALL_ERRORS.map(err => `'${err.name}'`).join(' | ')
type PossibleErrorNames = ALL_NAMES

这里的问题是,这是不正确的,因为这将 ALL_NAMES 返回为

"'No-empty-name' | 'No-empty-age' | 'No-under-18'" 这是一个string


显然,这是join() 的预期行为,但我有什么办法

  1. 将字符串转为表达式
  2. ALL_ERRORS 数组推断类型的一些可能更优雅的名称?

【问题讨论】:

  • 不,.join 和其他数组操作发生在运行时。运行时没有类型系统,它在编译时不复存在。您可以将数组作为类型,但它必须在编译时可转换,因此["a", "b", "c"] 可以转换为"a" | "b" | "c",但for (const string of something) arr.push(string) 不是。

标签: typescript types


【解决方案1】:

您可以使用as const assertions 并执行类似的操作,前提是在编译时知道完整的数组。

type ErrorObject = {
  name: string,
  message: string
}


const ALL_ERRORS = [
   {
     name: 'No-empty-name',
     message: 'You must provide a name',
   },
   {
     name: 'No-empty-age',
     message: 'You must provide an age',
   },
   {
     name: 'No-under-18',
     message: 'You must be 18+ to continue',
   }
] as const // <----- notice here

type PossibleErrorNames = typeof ALL_ERRORS[number]["name"]
// "No-empty-name" | "No-empty-age" | "No-under-18"

Playground

【讨论】:

    【解决方案2】:

    这样做的唯一方法是将所有错误列表翻转到一个对象中,这将允许您在编译时访问这些类型。你可以这样做:

    const AllErrors = {
      'No-empty-name': 'You must provide a name',
      'No-empty-age': 'You must provide an age',
      'No-under-18': 'You must be 18+ to continue',
    }
    
    // Rationalises to this: type AllErrorNames = "No-empty-name" | "No-empty-age" | "No-under-18"
    type AllErrorNames = keyof typeof AllErrors
    
    // And if you still need them in an array, you can use something like this
    const AllErrorsArray = Object.keys(AllErrors).map(errorName => ({name: errorName, message: AllErrors[errorName]}))
    

    【讨论】:

    • 我确实纠正了自己,@bugs 的答案是另一种唯一的方法;),const 断言对我来说是新的!如果您希望使您的代码尽可能接近现在,请使用他们的答案!
    猜你喜欢
    • 1970-01-01
    • 2012-09-22
    • 1970-01-01
    • 2018-07-10
    • 2016-03-22
    • 2012-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多