我记得当我看到每个API处理程序的类型定义完全解释了该函数所调用的所有副作用时,真的迷住了某个人的Haskell代码库。因此,我着手尝试使用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很生气,因为我有Language的递归类型定义。真是可惜。有什么解决方法吗?
我想做的下一件事是通过扩展Promises使用ES6异步/等待语法。它很杂乱,但是我想出了一种扩展Promises的方法。
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>
无论如何,我认为这种架构将非常强大。如果您能找到解决这些问题的方法,请告诉我。