考虑到下面的代码,在TypeScript中是否有更简洁的方法来表达许多嵌套的映射函数?对于这个用例,我喜欢Scala的“理解”,但是我在TypeScript中找不到相应的。我觉得我错过了一些很明显的东西。
由于验证原因,我有几个对象可能无法实例化,所以返回类型都是Either<string, T>。例如:
const userId: Either<string, UserId> = UserId.create('1234')当组合由上面的许多语句组成的对象时,很难看到。为了提高可读性,示例中的所有变量都被替换为字符串。
In TypeScript,,这就是我要做的。有没有更干净的方式来表达这一点而不失去我的类型?
const userSettings: Either<string, UserSettings> = UserId.create('1234').chain(userId => {
return Email.create('hello@world.com').chain(email => {
return Active.create(183).chain(active => {
return Role.create('admin').map(role => {
return UserSettings(userId, email, active, role)
})
})
})
})在Scala中,我可以这样表示上面的代码:
for {
userId <- UserId.create('1234')
email <- Email.create('hello@world.com')
active <- Active.create(183)
role <- Role.create('admin')
} yield UserSettings(userId, email, active, role)我正在使用净化库进行类型设置,比如。
有谁有任何技巧、建议和/或库可以帮助清理我嵌套的映射函数TypeScript混乱呢?
发布于 2021-08-22 03:39:32
你可以用这样的东西:
const userSettings = Right({})
.chain(acc => UserId.create('1234').map(userId => ({...acc, userId})))
.chain(acc => Email.create('hello@world.com').map(email => ({...acc, email})))
.chain(acc => Active.create(183).map(active => ({...acc, active})))
.chain(acc => Role.create('admin').map(role => ({...acc, role})))
.map(({userId, email, active, role}) => UserSettings(userId, email, active, role))您还可以定义一个助手函数:
// This implementation works for all functors, but the types only work for
// Either due to TypeScript's lack of HKTs
const bind =
<N extends string, A extends object, L, R>(
name: Exclude<N, keyof A>,
f: (acc: A) => Either<L, R>
) =>
(acc: A): Either<L, A & Record<N, R>> =>
f(acc).map(r => ({...acc, [name]: r} as A & Record<N, R>))
const userSettings: Either<string, UserSettings> = Right({})
.chain(bind('userId', () => UserId.create('1234')))
.chain(bind('email', () => Email.create('hello@world.com')))
.chain(bind('active', () => Active.create(183)))
.chain(bind('role', () => Role.create('admin')))
.map(({userId, email, active, role}) => UserSettings(userId, email, active, role))让bind接受一个函数可以实现这样的功能:
Right({})
.chain(bind('a', () => Right(1)))
// The value of b depends on a
.chain(bind('b', ({a}) => Right(a + 1)))
// a is 1 and b is 2
.map(({a, b}) => `a is ${a} and b is ${b}`)这几乎是一个港口的fp-ts实现do表示法,所以所有的信用归于朱利奥坎蒂和贡献者的fp-ts。
如果您经常编写() =>,您可以使用另一个助手:
// This could definitely be named better
const bind_ = <N extends string, A extends object, L, R>(
name: Exclude<N, keyof A>,
either: Either<L, R>
): ((acc: A) => Either<L, A & Record<N, R>>) => bind(name, () => either)
const userSettings: Either<string, UserSettings> = Right({})
.chain(bind_('userId', UserId.create('1234')))
.chain(bind_('email', Email.create('hello@world.com')))
.chain(bind_('active', Active.create(183)))
.chain(bind_('role', Role.create('admin')))
.map(({userId, email, active, role}) => UserSettings(userId, email, active, role))https://stackoverflow.com/questions/67524514
复制相似问题