Skip to content

Commit bbfab1d

Browse files
committed
Add truthy type guard
This adds an explicit type guard for truthiness. Generally you could use the `Boolean` constructor, but typescript doesn't treat that as a type guard right now (see [#16655](microsoft/TypeScript#16655)).
1 parent 02d0da8 commit bbfab1d

10 files changed

+36
-37
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "fast-ts-helpers",
3-
"version": "0.0.0-dev.4",
3+
"version": "0.0.0-dev.5",
44
"private": true,
55
"description": "A package containing some helpful functions, constants, types, and react hooks.",
66
"license": "MIT",

src/BooleanConstructor.d.ts

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/cacheKey.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import stringify from "json-stable-stringify"
22

3+
import { truthy } from "./truthy"
34
import { uuid } from "./uuid"
45

56
/**
@@ -35,7 +36,7 @@ export function cacheKey<Type>(obj: Type) {
3536
}
3637

3738
function hasCacheKey<T>(obj: T): obj is T & { __cacheKey: string } {
38-
return Boolean(obj) && `__cacheKey` in obj
39+
return truthy(obj) && `__cacheKey` in obj
3940
}
4041

4142
return stringify(obj, { replacer })

src/concatUrls.ts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,18 @@ import { FalsibleList } from "./FalsibleList"
44
/** Concatenate the passed URL parts into one URL using `/` as the separator, with a leadin slash and without a trailing slash. */
55
export function concatUrls(...urls: FalsibleList<string>[]) {
66
const filtered = flatfilter(urls)
7-
let protocol = ``
87

9-
const first = filtered[0]
8+
if (filtered.length > 0) {
9+
let protocol = ``
10+
const first = filtered[0]
1011

11-
if (Boolean(first) && /^https?:\/\//.test(first)) {
12-
protocol = /^https?:\//.exec(first)![0]
13-
filtered[0] = first.replace(/^https?:\/\//, ``)
12+
if (/^https?:\/\//.test(first)) {
13+
protocol = /^.+?:\/(?=\/)/.exec(first)![0]
14+
filtered[0] = first.replace(/^.+?:\/\//, ``)
15+
}
16+
17+
return `${protocol}/${filtered.join(`/`).replace(/^\/+|\/+$|\/+(\/)/g, `$1`)}`
1418
}
1519

16-
return (
17-
protocol +
18-
(filtered.length > 0
19-
? `/${filtered.join(`/`).replace(/^\/+|\/+$|\/+(\/)/g, `$1`)}`
20-
: ``)
21-
)
20+
return ``
2221
}

src/focus.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { focusableSelector } from "./focusableSelector"
21
import { Falsible } from "./Falsible"
2+
import { focusableSelector } from "./focusableSelector"
3+
import { truthy } from "./truthy"
34

45
/**
56
* Transfer the focus to an element, optionally preventing scrolling. You can use this to focus on elements that aren't actually focusable too, for accessibility.
@@ -8,7 +9,7 @@ import { Falsible } from "./Falsible"
89
* @param preventScroll Whether scrolling should be prevented. Defaults to `true`.
910
*/
1011
export function focus(element: Falsible<HTMLElement | SVGElement>, preventScroll = true) {
11-
if (!Boolean(element)) {
12+
if (!truthy(element)) {
1213
return
1314
}
1415

src/onEnterKeyDown.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Falsible } from "./Falsible"
2-
3-
import { onNonRepeatedKeyDown } from "./onNonRepeatedKeyDown"
42
import { memoize } from "./memoize"
3+
import { onNonRepeatedKeyDown } from "./onNonRepeatedKeyDown"
4+
import { truthy } from "./truthy"
55

66
/**
77
* Respond to the event where the user presses the enter key, responding only to the actula press, not the repeated events if the user holds it.
@@ -19,7 +19,7 @@ export const onEnterKeyDown = memoize(
1919
) => {
2020
return onNonRepeatedKeyDown(
2121
(event) => {
22-
if (event.key === `Enter` && Boolean(action)) {
22+
if (event.key === `Enter` && truthy(action)) {
2323
action(event)
2424
}
2525
},

src/onNonRepeatedKeyDown.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { Falsible } from "./Falsible"
2-
3-
import { memoize } from "./memoize"
41
import { combine } from "./combine"
52
import { deleteOwnProperties } from "./deleteOwnProperties"
3+
import { Falsible } from "./Falsible"
4+
import { memoize } from "./memoize"
5+
import { truthy } from "./truthy"
66

77
/** The keys that are currently pressed. */
88
const keysDown: Record<string, boolean> = {}
@@ -23,7 +23,7 @@ export const onNonRepeatedKeyDown = memoize(
2323
) => {
2424
return {
2525
onKeyDown: combine(
26-
Boolean(action) &&
26+
truthy(action) &&
2727
((event: React.KeyboardEvent) => {
2828
if (!keysDown[event.key]) {
2929
keysDown[event.key] = true
@@ -34,7 +34,7 @@ export const onNonRepeatedKeyDown = memoize(
3434
extraOnKeyDown
3535
),
3636
onKeyUp: combine(
37-
Boolean(action) &&
37+
truthy(action) &&
3838
((event: React.KeyboardEvent) => {
3939
if (event.key === `Meta`) {
4040
deleteOwnProperties(keysDown)

src/parseDate.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { truthy } from "./truthy"
2+
13
/**
24
* Parse a date, number, or string into a Date.
35
*
@@ -41,7 +43,7 @@ export function parseDate(date: string | number | Date | undefined) {
4143
}
4244

4345
// If the datePart is set, parse that date part
44-
if (Boolean(datePart)) {
46+
if (truthy(datePart)) {
4547
const [rawYear, rawMonth, rawDay] = datePart.split(`-`).map(parseFloat)
4648

4749
year = rawYear
@@ -58,7 +60,7 @@ export function parseDate(date: string | number | Date | undefined) {
5860
let zoneHalf: string | undefined
5961

6062
// If the time part is defined, get the hour, minute, and second values
61-
if (Boolean(timePart)) {
63+
if (truthy(timePart)) {
6264
const parts = timePart.split(/Z|\+|(?=-)/)
6365

6466
const timeHalf = parts[0]!
@@ -82,12 +84,12 @@ export function parseDate(date: string | number | Date | undefined) {
8284
const manualTime = new Date(year, month, day, hours, minutes, seconds).getTime()
8385

8486
// If the time part is defined, assume UTC. The other if statement below will offset accordingly.
85-
if (Boolean(timePart)) {
87+
if (truthy(timePart)) {
8688
offset = now.getTimezoneOffset() * 60 * 1000
8789
}
8890

8991
// If the zone half of the time part is defined, do that offset here
90-
if (Boolean(zoneHalf)) {
92+
if (truthy(zoneHalf)) {
9193
const [offsetHours, offsetMinutes] = zoneHalf.split(`:`).map(parseFloat)
9294

9395
offset += offsetHours * 60 * 60 * 1000

src/truthy.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { Falsible } from "./Falsible"
2+
3+
export function truthy<T>(value?: Falsible<T>): value is T {
4+
return Boolean(value)
5+
}

0 commit comments

Comments
 (0)