我不熟悉这个库,所以可能有一种更简洁、更简洁的方法,但是在我查看了@morphic-ts 的代码之后,我想到了这个:
import { summonFor } from '@morphic-ts/batteries/lib/summoner-ESBST';
import { EqType, EqURI } from '@morphic-ts/eq-interpreters';
import { IOTSType, IoTsURI } from '@morphic-ts/io-ts-interpreters';
import { ShowType, ShowURI } from '@morphic-ts/show-interpreters';
import * as Num from 'fp-ts/Number';
import { NumberFromString } from 'io-ts-types';
import type { AlgebraUnion } from '@morphic-ts/batteries/lib/program';
import type { M } from '@morphic-ts/batteries/lib/summoner-ESBST';
import type { HKT, Kind, URIS } from '@morphic-ts/common/lib/HKT';
import type { AnyEnv } from '@morphic-ts/common/lib/config';
import type { Eq } from 'fp-ts/Eq';
import type { Show } from 'fp-ts/Show';
import type { Type } from 'io-ts';
const customIoTs = <A, O>(
type: Type<A, O>,
eq: Eq<A>,
show: Show<A>
): (<Env extends AnyEnv>(F: AlgebraUnion<'HKT', Env>) => HKT<Env, O, A>) => {
const ioTsInterp: Kind<IoTsURI, AnyEnv, O, A> = () => new IOTSType(type);
const eqInterp: Kind<EqURI, AnyEnv, O, A> = () => new EqType(eq);
const showInterp: Kind<ShowURI, AnyEnv, O, A> = () => new ShowType(show);
return (<Env extends AnyEnv>(
F: AlgebraUnion<URIS, Env>
): Kind<URIS, Env, O, A> =>
F._F === IoTsURI
? ioTsInterp
: F._F === EqURI
? eqInterp
: F._F === ShowURI
? showInterp
: ((): never => {
throw new Error(`Unknown URI: ${F._F}`);
})()) as <Env extends AnyEnv>(
F: AlgebraUnion<'HKT', Env>
) => HKT<Env, O, A>;
};
interface IAppEnv {}
const { summon } = summonFor<IAppEnv>({});
const numberFromString = customIoTs(NumberFromString, Num.Eq, Num.Show);
// Usage
const Foo = summon(F => numberFromString(F));
Foo.type.decode('123'); // Right 123
Foo.type.decode('abc'); // Left [...]
Foo.eq.equals(Foo.build(1), Foo.build(1)); // true
Foo.show.show(Foo.build(1)); // '1'
const Bar = summon(F => F.interface({ num: numberFromString(F) }, 'Bar'));
Bar.strictType.decode({ num: '123' }); // Right { num: 123 }
Bar.strictType.decode({ num: 'abc' }); // Left [...]
Bar.eq.equals(Bar.build({ num: 1 }), Bar.build({ num: 1 })); // true
Bar.show.show(Bar.build({ num: 1 })); // '{ num: 1 }'
说明
ESBST 召唤者使用不同的“解释器”(summon(F => ...) 中的 F)多次调用您传递给 summon (program) 的函数:
const { create, type } = program(modelIoTsNonStrictInterpreter<NonNullable<R>>())(env)
return {
build: a => a,
eq: program(modelEqInterpreter<NonNullable<R>>())(env).eq,
show: program(modelShowInterpreter<NonNullable<R>>())(env).show,
strictType: program(modelIoTsStrictInterpreter<NonNullable<R>>())(env).type,
type,
create
}
@morphic-ts/batteries/src/summoner-ESBST.ts
(顺便说一下,上面的R是summonFor的泛型参数(例如IAppEnv)。)
这里定义了“原始”的modelIoTs(Non)StrictInterpreters:
export const ioTsPrimitiveInterpreter = memo(
<Env extends AnyEnv>(): ModelAlgebraPrimitive<IoTsURI, Env> => ({
_F: IoTsURI,
date: config => env => new IOTSType(iotsApplyConfig(config)(DateFromISOString, env, {})),
boolean: config => env => new IOTSType(iotsApplyConfig(config)(t.boolean, env, {})),
string: config => env => new IOTSType(iotsApplyConfig(config)(t.string, env, {})),
number: config => env => new IOTSType(iotsApplyConfig(config)(t.number, env, {})),
// some more omitted for brevity
@morphic-ts/io-ts-interpreters/src/model/primitives.ts
这里的重要部分是new IOTSType。我的NumberFromString 不需要任何configuration,所以我在实现中跳过了iotsApplyConfig 部分。
这对于modelEqInterpreter 和modelShowInterpreter 非常相似,除了分别使用EqType 和ShowType:
export const eqPrimitiveInterpreter = memo(
<Env extends AnyEnv>(): ModelAlgebraPrimitive<EqURI, Env> => ({
_F: EqURI,
date: config => env =>
new EqType(
eqApplyConfig(config)(
eq.contramap(eqNumber, (date: Date) => date.getTime()),
env,
{}
)
),
boolean: config => env => new EqType(eqApplyConfig(config)(eqBoolean, env, {})),
string: config => env => new EqType(eqApplyConfig(config)(eqString, env, {})),
number: config => env => new EqType(eqApplyConfig(config)(eqNumber, env, {})),
@morphic-ts/eq-interpreters/src/model/primitives.ts
export const showPrimitiveInterpreter = memo(
<Env extends AnyEnv>(): ModelAlgebraPrimitive<ShowURI, Env> => ({
_F: ShowURI,
date: config => env => new ShowType(showApplyConfig(config)({ show: (date: Date) => date.toISOString() }, env, {})),
boolean: config => env => new ShowType(showApplyConfig(config)(showBoolean, env, {})),
string: config => env => new ShowType(showApplyConfig(config)(showString, env, {})),
number: config => env => new ShowType(showApplyConfig(config)(showNumber, env, {})),
@morphic-ts/show-interpreters/src/model/primitives.ts