如何使幻像类型与 TypeScript 中的方法一起使用?
How to make phantom types work with methods in TypeScript?
考虑以下使用虚拟类型的程序:
const strlen = (str: string) => str.length;
type Const<A, B> = { type: 'Const', value: A };
const Const = <A, B = never>(value: A): Const<A, B> => ({ type: 'Const', value });
const map = <A, B, C>(_f: (value: B) => C, { value }: Const<A, B>): Const<A, C> => Const(value);
const contramap = <A, B, C>(_f: (value: C) => B, { value }: Const<A, B>): Const<A, C> => Const(value);
const constant = Const(true);
map(strlen, constant); // works
contramap(strlen, constant); // works
上面的程序类型检查是因为推断出了正确的类型。 constant
值具有推断类型 Const<boolean, never>
。 map
函数以 A = boolean
、B = string
和 C = number
类型调用。 contramap
函数以 A = boolean
、B = number
和 C = string
.
类型调用
但是,最好使用方法而不是函数来编写上述表达式。因此,我尝试了以下方法:
const strlen = (str: string) => str.length;
interface Const<A, B> {
map: <C>(f: (value: B) => C) => Const<A, C>;
contramap: <C>(f: (value: C) => B) => Const<A, C>;
}
const Const = <A, B = never>(value: A): Const<A, B> => ({
map: () => Const(value),
contramap: () => Const(value)
});
const constant = Const(true);
constant.map(strlen); // works
constant.contramap(strlen); // error
如您所见,map
方法有效,但 contramap
方法无效。这是因为 constant
的类型是 Const<boolean, never>
并且它没有被方法调用细化,即对于 map
类型没有细化为 Const<boolean, string>
而对于 contramap
类型未细化为 Const<boolean, number>
.
因此,map
或 contramap
中的一个有效,但不能同时有效。如果对象的类型是 Const<boolean, never>
则 contramap
不起作用。如果对象的类型是 Const<boolean, unknown>
那么 map
不起作用。
如何使用方法而不是函数使 map
和 contramap
都工作?
我通过将 Const
接口的类型参数 B
设为幻像类型来解决这个问题。
const strlen = (str: string) => str.length;
interface Const<A, B> {
map: <B, C>(f: (value: B) => C) => Const<A, C>;
contramap: <B, C>(f: (value: C) => B) => Const<A, C>;
}
const Const = <A, B = never>(value: A): Const<A, B> => ({
map: () => Const(value),
contramap: () => Const(value)
});
const constant = Const(true);
constant.map(strlen); // works
constant.contramap(strlen); // works
Const
接口的类型参数 B
现在被 map
和 contramap
的类型参数隐藏。这是有道理的,因为 Const
接口的类型参数 B
是幻像类型。因此,不应使用它。另一方面,map
和 contramap
的调用者应该能够决定类型参数 B
应该被实例化的类型。
考虑以下使用虚拟类型的程序:
const strlen = (str: string) => str.length;
type Const<A, B> = { type: 'Const', value: A };
const Const = <A, B = never>(value: A): Const<A, B> => ({ type: 'Const', value });
const map = <A, B, C>(_f: (value: B) => C, { value }: Const<A, B>): Const<A, C> => Const(value);
const contramap = <A, B, C>(_f: (value: C) => B, { value }: Const<A, B>): Const<A, C> => Const(value);
const constant = Const(true);
map(strlen, constant); // works
contramap(strlen, constant); // works
上面的程序类型检查是因为推断出了正确的类型。 constant
值具有推断类型 Const<boolean, never>
。 map
函数以 A = boolean
、B = string
和 C = number
类型调用。 contramap
函数以 A = boolean
、B = number
和 C = string
.
但是,最好使用方法而不是函数来编写上述表达式。因此,我尝试了以下方法:
const strlen = (str: string) => str.length;
interface Const<A, B> {
map: <C>(f: (value: B) => C) => Const<A, C>;
contramap: <C>(f: (value: C) => B) => Const<A, C>;
}
const Const = <A, B = never>(value: A): Const<A, B> => ({
map: () => Const(value),
contramap: () => Const(value)
});
const constant = Const(true);
constant.map(strlen); // works
constant.contramap(strlen); // error
如您所见,map
方法有效,但 contramap
方法无效。这是因为 constant
的类型是 Const<boolean, never>
并且它没有被方法调用细化,即对于 map
类型没有细化为 Const<boolean, string>
而对于 contramap
类型未细化为 Const<boolean, number>
.
因此,map
或 contramap
中的一个有效,但不能同时有效。如果对象的类型是 Const<boolean, never>
则 contramap
不起作用。如果对象的类型是 Const<boolean, unknown>
那么 map
不起作用。
如何使用方法而不是函数使 map
和 contramap
都工作?
我通过将 Const
接口的类型参数 B
设为幻像类型来解决这个问题。
const strlen = (str: string) => str.length;
interface Const<A, B> {
map: <B, C>(f: (value: B) => C) => Const<A, C>;
contramap: <B, C>(f: (value: C) => B) => Const<A, C>;
}
const Const = <A, B = never>(value: A): Const<A, B> => ({
map: () => Const(value),
contramap: () => Const(value)
});
const constant = Const(true);
constant.map(strlen); // works
constant.contramap(strlen); // works
Const
接口的类型参数 B
现在被 map
和 contramap
的类型参数隐藏。这是有道理的,因为 Const
接口的类型参数 B
是幻像类型。因此,不应使用它。另一方面,map
和 contramap
的调用者应该能够决定类型参数 B
应该被实例化的类型。