作为练习,我将在打字稿中实现简单的代数数据类型。
我想实现ap
函数,以使其仅在包装器包含函数时才可调用。
这就是我想要使用它的方式:
Identity.of(x => x + 1).ap(Identity.of(5)); // This should work and return an Identity(6)
Identity.of(5).ap(...); // This should make the compiler emit an error
我试图用通用条件来定义它:
type Func<T, U> = (p: T) => U;
interface Identity<T> {
map<U>(fn: Func<T, U>): Identity<U>;
ap<U, V>(id: Identity<U>): T extends Func<U, V> ? Identity<V> : never;
}
function of<T>(value: T): Identity<T> {
return {
map: fn => of(fn(value)),
ap: id => id.map(value)
};
}
但是出现以下错误:
Type 'Identity<V>' is not assignable to type 'T extends Func<U, V> ? Identity<V> : never'.(2322)
input.ts(5, 3): The expected type comes from the return type of this signature.
Argument of type 'T' is not assignable to parameter of type 'Func<U, V>'.
这在打字稿中有可能做到吗?
答案 0 :(得分:0)
经过长时间的暴力破解后,我找到了使这种类型变得安全的方法:
type Func<T, U> = (p: T) => U;
interface Identity<T> {
map<U>(fn: Func<T, U>): Identity<U>;
ap(id: T extends Func<infer U, any> ? Identity<U> : never): T extends Func<any, infer U> ? Identity<U> : never;
}
function of<T>(value: T): Identity<T> {
return {
map: fn => of(fn(value)),
ap: id => {
const fn: unknown = value;
return id.map(fn as Func<unknown, unknown>) as T extends Func<any, infer U> ? Identity<U> : never;
}
};
}
推断接口定义中的类型有助于链式操作在链接操作时保存类型,并使所有类型安全。 在编写实现时,我们必须使用typecast来帮助打字稿。