【发布时间】:2020-05-30 03:13:25
【问题描述】:
挑战
以下是我们如何在应用中控制和传递数据的简化示例。它用于许多地方,用于在 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
}
}
【问题讨论】:
-
您是说希望能够让 typescript 推断出在任意类型的对象通过函数转换这些键之后您将拥有哪些键?
-
@OliverRadini,是的!这是更好的表达方式。 :)
标签: javascript typescript class