首页 > 解决方案 > 获取返回对象的 Typescript 类型

问题描述

挑战

下面是我们如何在应用程序中控制和传递数据的简化示例。它用于许多地方,用于在 UI、API 和数据库之间转换数据。API 和 UI 使用camelCase。数据库使用snake_case.

目前,这是一个尴尬的 Partial/Pick 类型组合,以便在哪里打字......

const item = { fooBar: 'something' }
Item.cast(item).value // returns type Partial<ItemModel>
Item.create(item).value // returns type ItemModel

目标是返回真正返回的对象类型。

// Examples
const item = { fooBar: 'something' }
Item.cast(item).value // returns { fooBar: 'something' }
Item.cast(item).databaseFormat // returns { foo_bar: 'something' }
Item.create(item).value // returns { id: '{uuid}', fooBar: 'something' }
Item.create(item).databaseFormat // returns { id: '{uuid}', foo_bar: 'something' }

const itemFromDatabase = { id: '{uuid}', foo_bar: 'something', baz: null }
Item.cast(itemFromDatabase).value // returns { id: '{uuid}', fooBar: 'something', baz: null }
Item.cast(itemFromDatabase).databaseFormat // returns { id: '{uuid}', foo_bar: 'something', baz: null }

对此有什么想法吗?我会想象它类似于 Object.entries() 返回类型,但我无法找出正确的T keyof组合。

// https://mariusschulz.com/blog/keyof-and-lookup-types-in-typescript
interface ObjectConstructor {
  // ...
  entries<T extends { [key: string]: any }, K extends keyof T>(o: T): [keyof T, T[K]][];
  // ...
}

代码

import camelcaseKeys from 'camelcase-keys'
import snakecaseKeys from 'snakecase-keys'

type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>

interface ItemModel {
  id: string
  fooBar: any
  baz?: number
}

interface ItemDatabaseModel {
  id: string
  foo_bar: any
  baz?: number
}

export class Item {

  private _data: Partial<ItemModel>

  public static cast(item: Partial<ItemModel | ItemDatabaseModel>): Item {
    return new this(camelcaseKeys(item))
  }

  public static create(item: Optional<ItemModel | ItemDatabaseModel, 'id'>): Item {
    // Use item.id or add item.id === null to force key
    return new this(camelcaseKeys(item))
  }

  private constructor(input: Partial<ItemModel>) {
    // Validate "input" properties have a Item class property setter, else throw
    // foreach => this[key] = input[key]
  }

  get databaseFormat() { return snakecaseKeys(this._data) }
  get value() { return this._data }

  set id(value: string | null) {
    // automatically generate an ID if null, otherwise validate
    this._data.id = value
  }
  set fooBar(value: any) {
    // validate
    this._data.fooBar = value
  }
  set baz(value: number | null) {
    // validate
    this._data.baz = value
  }
}

标签: javascripttypescriptclass

解决方案


实现此目的的最佳方法是使用一些工具,该工具通过您的 typescript 文件查找类型/接口定义,并根据找到的内容自动输出带有转换键的其他类型。

目前打字稿不支持通过类型定义自动进行这种转换,我猜他们对添加类似的东西会非常谨慎;例如,在类型定义中连接字符串文字尚未成为语言,不幸的是,您在这里寻找的东西比这要复杂得多。


推荐阅读