我记得我浏览过某人的Haskell代码库,当我看到每个API处理程序的类型定义完全解释了函数调用的所有副作用时,我非常感兴趣。所以我开始尝试用TypeScript完成类似的事情。这是我目前所拥有的。
export type More = {
then: (...args: any[]) => Thenable
}
type Done = {
then: undefined
}
const done = { then: undefined } as Done
type Thenable = More | Done
type RedisRead<Key extends string, Then extends Thenable> = {
type: "RedisRead"
key: Key
then: (value: string) => Then
}
type RedisWrite<Key extends string, Then extends Thenable> = {
type: "RedisWrite"
key: Key
value: string
then: () => Then
}
function readRedis<Key extends string, Then extends Thenable>(
key: Key,
then: (value: string) => Then
): RedisRead<Key, Then> {
return {
type: "RedisRead",
key: key,
then: then,
}
}
function writeRedis<Key extends string, Then extends Thenable>(
key: Key,
value: string,
then: () => Then
): RedisWrite<Key, Then> {
return {
type: "RedisWrite",
key: key,
value: value,
then: then,
}
}
// Inferred type: RedisRead<"x", RedisWrite<"y", Done>>
function doSomething() {
return readRedis("x", value => {
return writeRedis("y", value, () => {
return done
})
})
}
上面代码的结果是
doSomething
RedisRead<"x", RedisWrite<"y", Done>>
. 很干净对吧!
type Language = Done | RedisRead<any, Language> | RedisWrite<any, Language>
function interpret(item: Language) {
if (item.type === "Done") {
console.log("Done")
} else if (item.type === "RedisWrite") {
redis.add(key, value, () => interpret(item.then()))
} else if (item.type === "RedisRead") {
redis.get(key, (value) => interpret(item.then(value)))
}
}
然而,TypeScript很生气我有一个递归的语言类型定义。真倒霉。有办法绕过这件事吗?
接下来我想做的是通过扩展Promises来使用ES6 async/await语法。这很混乱,但我想出了一个延长承诺的方法。
class RedisRead<Key extends string, T extends Promise<any>> extends Promise<
string
> {
resolve: (value: any) => void
reject: (value: any) => void
constructor(public key: Key) {
super((resolve, reject) => {
this.resolve = resolve
this.reject = reject
})
}
type: "RedisRead"
then: (fn?: null | ((value: string) => T)) => T
}
class RedisWrite<Key extends string, T extends Promise<any>> extends Promise<
string
> {
resolve: (value: any) => void
reject: (value: any) => void
constructor(public key: Key, public value: string) {
super((resolve, reject) => {
this.resolve = resolve
this.reject = reject
})
}
type: "RedisWrite"
then: (fn?: null | ((value: any) => T)) => T
}
async function doSomething() {
const value = await new RedisRead("x")
return new RedisWrite("y", value)
}
它似乎起作用,但推断的类型是
Promise<any>
不管怎样,我认为这个架构会非常强大。如果你能解决这些问题,请告诉我。