需求: 实现一个方法,给定某个公民,判断他是否是美国18岁以下的公民,符合条件,返回该公民。
一般的实现方式:
const checkEligibility = (person: Person) => {
// 检查年龄
if (!person.age <= 18) {
return "The Person's age must be under 18";
};
// 检查国籍
if (person.nationality !== 'American') {
return "The Person's must be a U.S. citizen";
};
return person;
};
fromPredicate
函数签名
export declare const fromPredicate: {
<A, B extends A, E>(refinement: Refinement<A, B>, onFalse: (a: A) => E): (a: A) => Either<E, B>
<A, E>(predicate: Predicate<A>, onFalse: (a: A) => E): <B extends A>(b: B) => Either<E, B>
<A, E>(predicate: Predicate<A>, onFalse: (a: A) => E): (a: A) => Either<E, A>
}
接受两个函数,第一个函数判断数据是否符合规则,第二个函数,当不符合规则后,要执行的函数,如果符合规则,则返回E.right(1),如果不符合规则,返回E.left(1)
const person1 = {
name: "Tom",
age: 10,
};
const person2 = {
name: "Tom",
age: 20,
};
const isAdult = E.fromPredicate<number, string>(
(age) => age >= 18,
() => "未成年"
);
console.log(isAdult(person1.age)); // { _tag: 'Left', left: '未成年' }
console.log(isAdult(person2.age)); // { _tag: 'Right', right: 20 }
案例1
type Person = {
name: string;
age: number;
nationality: string;
gender?: string;
};
const checkEligibility = (person: Person) =>
pipe(
person,
// check the age first by using fromPredicate
E.fromPredicate(
(person) => person.age <= 18,
() => "The Person's age must be under 18"
),
// check the nationality next
E.chain(
E.fromPredicate(
(person) => person.nationality === "American",
() => "The Person must be a U.S. citizen"
)
)
);
const toUpperCasePerson = (person: Person) => ({
...person,
name: person.name.toUpperCase(),
nationality: person.nationality.toUpperCase(),
});
const femaleOnly = (person: Person) =>
pipe(
person,
// check if gender is provided
E.fromPredicate(
(person) => !!person.gender,
() => "Please provide your gender"
),
// Check if gender is female
// Since the data return from the above E.fromPredicate is an Either
// We use chain (Flatten Map) to flatten Either<Either<string, Person>> into <Either<string, Person>
E.chain((person) =>
pipe(
person,
E.fromPredicate(
(p) => p.gender === "female",
() => "Female only"
)
)
)
);
// Same as above, but less codes
const femaleOnlyV2 = flow(
E.fromPredicate(
(person: Person) => !!person.gender,
() => "Please provide your gender"
),
E.chain(
E.fromPredicate(
(person) => person.gender === "female",
() => "Female Only"
)
)
);
const personA = {
name: "A",
age: 18,
gender: "female",
nationality: "American",
} as Person;
const result = pipe(
personA,
checkEligibility,
// Since toUpperCasePerson has the type signature Person -> Person,
// We use map to unwrap the Person from Either, then return Person wrapped in Either
E.map(toUpperCasePerson),
// Since femaleOnly has the type signature Person -> Either<string, Person>
// We use chain to unwrap the Person from Either, return Person wrapped in either and then flatten it
E.chain(femaleOnlyV2),
E.getOrElseW((e) => e)
);
console.log(result, "result");
案例2
type Nullable<T> = T | undefined | null;
const getValidStringEither = (field: Nullable<string>) =>
pipe(
field,
// return Error if null or undefined
E.fromNullable(new Error(`${field} is null or undefined`)),
// return Error if the type is not string
E.chain(
E.fromPredicate(
string.isString,
(value) => new Error(`${value} is not a string`)
)
)
);
// Curry the function so we can partial apply the regex
const getValidEmailFormat = (regex: RegExp) => (email: string) =>
pipe(
email,
E.fromPredicate(
(value) => regex.test(value),
(value) => new Error(`"${value}" is not in a valid email format`)
)
);
// Partially apply the default validation regex
const getValidEmailUsingDefaultRegex = getValidEmailFormat(
/^[^\s@]+@[^\s@]+\.[^\s@]+$/
);
// Create an Either type and wrap the data inside Either because it can be an error or the data itself
const nameEither = getValidStringEither("myName");
/**
* For email, first check if it is a valid string or not, then check if it is a valid email or not
* We compose the functions here using 'flow'
* And we use "chain" because we want to flatten the result
*/
const getValidEmailAddressesE = flow(
getValidStringEither,
E.chain(getValidEmailUsingDefaultRegex)
);
const emailEither = getValidEmailAddressesE("myemail@email.com");
// console.log(emailEither);