When I create a luxon DateTime object from the fromISO
method, seems like it cannot be assigned to DateTime
properly.
e.g.
let datetime: DateTime = DateTime.fromISO(isoString);
does not compile with the error:
Type 'DateTime<true> | DateTime<false>' is not assignable to type 'DateTime<boolean>'.
Type 'DateTime<true>' is missing the following properties from type 'DateTime<boolean>': isWeekend, localWeekday, localWeekNumber, localWeekYear, weeksInLocalWeekYear
What is a proper way to construct DateTime from ISO string?
This is a somewhat complicated one. I'll provide some explanation below, but — in short — you can avoid the issue either by:
not using an explicit annotation, and instead simply rely on TypeScript's inference when initializing your variable:
import { DateTime } from "luxon";
const isoString = "2017-05-15T08:30:00";
let dt = DateTime.fromISO(isoString);
// ^? let dt: DateTime<true> | DateTime<false>
or you can use the exported type alias DateTimeMaybeValid
to annotate the instance:
import { DateTime, type DateTimeMaybeValid } from "luxon";
const isoString = "2017-05-15T08:30:00";
let dt: DateTimeMaybeValid = DateTime.fromISO(isoString);
// ^? let dt: DateTime<true> | DateTime<false>
More:
Luxon has a concept of validity. By default, DateTimes fail silently instead of throwing exceptions, and the validity information is stored on the DateTime instances. Here's an example:
<script type="module">
import { DateTime } from "https://cdn.jsdelivr.net/npm/luxon@3.4.4/build/es6/luxon.js";
for (const input of ["2017-05-15T08:30:00", "tomorrow"]) {
const dt = DateTime.fromISO(input);
const { isValid, invalidExplanation, invalidReason } = dt;
const text = dt.toString();
console.log({ input, isValid, invalidReason, invalidExplanation, text });
}
</script>
However, Luxon can be configured to throw in cases where an invalid DateTime would be produced:
<script type="module">
import { DateTime, Settings } from "https://cdn.jsdelivr.net/npm/luxon@3.4.4/build/es6/luxon.js";
Settings.throwOnInvalid = true;
for (const input of ["2017-05-15T08:30:00", "tomorrow"]) {
try {
const dt = DateTime.fromISO(input);
const text = dt.toString();
console.log({ input, valid: true, text });
} catch (cause) {
console.log({ input, valid: false });
console.error(cause);
}
}
</script>
In the type system, DateTime
is generic in an attempt to encode the validity state so that the compiler can use it to narrow:
for (const input of ["2017-05-15T08:30:00", "tomorrow"]) {
const dt = DateTime.fromISO(input);
if (dt.isValid) {
dt.invalidReason
// ^? (property) DateTime<true>.invalidReason: null
dt.invalidExplanation
// ^? (property) DateTime<true>.invalidExplanation: null
} else {
dt.invalidReason
// ^? (property) DateTime<false>.invalidReason: string
dt.invalidExplanation
// ^? (property) DateTime<false>.invalidExplanation: string | null
}
}
Note: You can inform the TypeScript compiler that you enabled the exception-throwing option (above): 1, 2