fromNullable
函数签名
export declare const fromNullable: <A>(a: A) => Option<NonNullable<A>>
如果传入的值是
undefined或null,则返回O.none,否则返回O.some(data)
expect(O.fromNullable(undefined)).toStrictEqual(O.none);
expect(O.fromNullable(null)).toStrictEqual(O.none);
expect(O.fromNullable(1)).toStrictEqual(O.some(1));
expect(O.fromNullable("")).toStrictEqual(O.some(""));
expect(O.fromNullable("sss")).toStrictEqual(O.some("sss"));
expect(O.fromNullable([])).toStrictEqual(O.some([]));
expect(O.fromNullable({ name: "Tom", age: 12 })).toStrictEqual(O.some({ name: "Tom",age: 12 }));
map
函数签名
// f:(a: A -> b: B) -> Option<a: A> -> Option<b: B>
export declare const map: <A, B>(f: (a: A) => B) => (fa: Option<A>) => Option<B>
- 接收一个判断函数,
f:(A) => B- 返回一个函数
(fa: Option<A>) => Option<B>- 当
fa是O.none,返回O.none- 当
fa是O.some,将O.some内包装的value传递给f:(A) => B- 将得到的
B使用O.Option包装为O.some返回
const isOdd = (n: number) => n % 2 !== 0;
const isOddOption = O.map(isOdd); // (fa: O.Option<number>) => O.Option<boolean>
const result1 = isOddOption(O.some(10)); // O.some(false)
expect(result).toStrictEqual(O.some(false));
const result2 = isOddOption(O.none); // O.none
expect(result2).toStrictEqual(O.none);
组合案例
单纯使用pipe时会遇到这样的问题
interface Foo{
bar: string
}
const foo1 = {
bar: 'hello'
} as Foo | undefined
pipe(foo1, (f) => f?.bar) // hello
const foo2 = undefined;
pipe(foo2, (f) => f?.bar) // undefined
这里的匿名函数的参数名称的命名,貌似用什么名称都不大正确,所以可以使用结构方式
interface Foo{
bar: string
}
const foo1 = {
bar: 'hello'
} as Foo | undefined
pipe(foo1, ({ bar }) => bar) // hello
const foo2 = undefined;
pipe(foo2, ({ bar }) => bar) // 这里会报错
为了既解决参数命名问题和报错问题,此时可以使用fromNullable和map来实现
interface Foo{
bar: string
}
const foo1 = {
bar: 'hello'
} as Foo | undefined
pipe(
foo1,
O.fromNullable, // { _tag: 'Some', value: { bar: 'hello' } }
O.map(({ bar }) => bar) // { _tag: 'Some', value: 'hello' }
)
pipe(
undefined,
O.fromNullable, // { _tag: 'None' }
O.map(({ bar }) => bar) // { _tag: 'None' }
)
flatten
函数签名
// 扁平化`Option`,将`Options<Option<A>>`转化为`Option<A>`
export declare const flatten: <A>(mma: Option<Option<A>>) => Option<A>
在遇到逐层嵌套的undefined的时候,就需要用到flatten:
interface Fizz {
buzz: string
}
interface Foo {
bar?: Fizz
}
const foo = { bar: undefined } as Foo | undefined
pipe(foo, (f) => f?.bar?.buzz) // undefined
可以使用O.fromNullable将foo和bar都变成Option类型:
import * as O from "fp-ts/Option";
pipe(
foo,
O.fromNullable,
O.map(({ bar }) =>
pipe(
bar,
O.fromNullable,
O.map(({ buzz }) => buzz),
)
),
) // { _tag: 'Some', value: { _tag: 'None' } }
最后得到的是一个嵌套的Option,但是我们想要得到的是一个不嵌套的Option,此时就需要用到O.flatten来展开。
import * as O from "fp-ts/Option";
pipe(
foo,
O.fromNullable,
O.map(({ bar }) =>
pipe(
bar,
O.fromNullable,
O.map(({ buzz }) => buzz),
)
),
O.flatten,
) // { _tag: "None" }
chain(Flatmap)
函数签名
export declare const chain: <A, B>(f: (a: A) => Option<B>) => (ma: Option<A>) => Option<B>
chain的作用就是将map和flatten这两步操作合并成一步。
import * as O from "fp-ts/Option";
pipe(
foo,
O.fromNullable,
O.map(({ bar }) => bar),
O.chain(
flow(
O.fromNullable,
O.map(({ buzz }) => buzz),
)
)
) // { _tag: "None" }
// const addressOption = O.fromNullable(person.address); // Option<string>
// const addressLine2Option = O.map<Address, O.Option<string>>(
// (address) => O.fromNullable(address.addressLine2) // Option<string>
// )(addressOption); // Option<Option<string>>
// const addressLine2OptionForreal = O.flatten(addressLine2Option); // Option<string>
// 以上代码等同于
const addressOption = O.fromNullable(person.address);
const addressLine2Option = O.chain<Address, string>(
(address) => O.fromNullable(address.addressLine2)
)(addressOption);
最终代码:
type Address = {
addressLine1: string;
addressLine2?: string;
};
type Person = {
name: string;
age: number;
address?: Address;
};
const personA = {
name: "A",
age: 20,
address: {
addressLine1: "hehe",
addressLine2: undefined,
},
};
const getAddressLine2Final = flow(
O.fromNullableK((person: Person) => person.address),
O.chain((address) => O.fromNullable(address.addressLine2)),
O.getOrElse(() => "none")
);
const result = pipe(personA, getAddressLine2Final);
console.log(result);