-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathvalidations.ts
More file actions
65 lines (53 loc) · 2.41 KB
/
Copy pathvalidations.ts
File metadata and controls
65 lines (53 loc) · 2.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// check this blog post for more insight on what's happening here: https://rubenpieters.github.io/programming/typescript/2018/07/13/existential-types-typescript.html
type RawValidation<A, B> = {
preprocess: (_: A) => B | null
getError: (_: B) => ErrorMessage
}
type MyError = { error: string }
type ErrorMessage = MyError | "allfine"
/**
* Like {@link RawValidation}, but it hides its second generic argument B.
* This is needed to have a list of validations with the same input A, but
* different intermediate values B, for example `Array<Validation<string>>`
*/
type Validation<A> =
(callback: <B>(rawValidation: RawValidation<A, B>) => ErrorMessage | null)
=> ErrorMessage | null
type MakeValidation = <A, B>(validation: RawValidation<A, B>) => Validation<A>
const applyTo = <A>(input: A) => <B>(func: (input: A) => B): B => func(input)
const makeValidation: MakeValidation = applyTo
const runRawValidation: <A>(toValidate: A) => <B>(validation: RawValidation<A, B>) => ErrorMessage | null =
toValidate => rawValidation => {
const preprocessed = rawValidation.preprocess(toValidate);
return preprocessed === null ? null : rawValidation.getError(preprocessed);
}
const runValidations: <A>(toValidate: A) => (validations: Array<Validation<A>>) => Array<ErrorMessage | null> =
toValidate => validations =>
validations.map(applyTo(runRawValidation(toValidate)))
const validations: Array<Validation<string>> = [
makeValidation({
preprocess: s => s[0],
getError: first => first === "o" ? { error: "should not start with 'o'" } : "allfine"
}),
makeValidation({
preprocess: s => s.length,
getError: len => len > 3 ? { error: "too long" } : "allfine"
})
]
function isNotNull<T>(value: T | null): value is T {
return value !== null
}
function isError(message: ErrorMessage): message is MyError {
return typeof message === "object"
}
const asText: (messages: Array<ErrorMessage | null>) => string =
validationResults => {
const validationsThatRan = validationResults.filter(isNotNull)
const errors = validationsThatRan.filter(isError).map(error => error.error)
return `I had ${validationResults.length}\
validations and ran ${validationsThatRan.length}\
of them. ${errors.length}\
returned errors. Here are all errors:\
${errors.join(", ")}`
}
console.log(asText(runValidations('A string to test')(validations)))