-
-
Save aperkaz/580e72b98eba5afac30549387562655d to your computer and use it in GitHub Desktop.
Typing date strings in TypeScript π
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {DateTime} from 'luxon' | |
type oneToNine = 1|2|3|4|5|6|7|8|9 | |
type zeroToNine = 0|oneToNine | |
/** | |
* Years | |
*/ | |
type YYYY = `19${zeroToNine}${zeroToNine}` | `20${zeroToNine}${zeroToNine}` | |
/** | |
* Months | |
*/ | |
type MM = `0${oneToNine}` | `1${0|1|2}` | |
/** | |
* Days | |
*/ | |
type DD = `${0}${oneToNine}` | `${1|2}${zeroToNine}` | `3${0|1}` | |
/** | |
* YYYYMMDD | |
*/ | |
type RawDateString = `${YYYY}${MM}${DD}`; | |
const date: RawDateString = '19990223' // β | |
const dateInvalid: RawDateString = '19990231' //β π 31st of February is not a valid date, but the template literal doesnt know! | |
const dateWrong: RawDateString = '19990299' // β Type error, 99 is not a valid day | |
// π Nominal type | |
type Brand<K, T> = K & { __brand: T }; | |
type DateString = Brand<string, 'DateString'>; | |
/** | |
* Use `moment`, `luxon` or other libraries | |
*/ | |
const isValidDate = (str: string): boolean => { | |
return DateTime.fromISO(str).invalid === null | |
}; | |
// π User-defined type guard | |
function isValidDateString(str: string): str is DateString { | |
return str.match(/^\d{4}\d{2}\d{2}$/) !== null && isValidDate(str); | |
} | |
// π Factory function | |
function toDateString(str: RawDateString): DateString { | |
if (isValidDateString(str)) return str; | |
throw new Error(`Invalid date string: ${str}`); | |
} | |
/** | |
* π Usage in type narrowing | |
*/ | |
// 1οΈβ£ valid string format, valid date | |
const date: string = '19990223'; | |
if (isValidDateString(date)) { | |
// evaluates to true, `date` is narrowed to type `DateString` β | |
} | |
// 2οΈβ£ valid string format, valid date (February doenst have 31 days) | |
const dateWrong: string = '19990231'; | |
if (isValidDateString(dateWrong)) { | |
// evaluates to false, `dateWrong` is not a valid date, even if its shape is YYYYMMDD β | |
} | |
/** | |
* π Usage in factory function | |
*/ | |
// 1οΈβ£ valid date | |
const date1 = toDateString('19990211'); | |
// `date1`, is of type `DateString` | |
// 2οΈβ£ invalid date-string format | |
const date2 = toDateString('asdf'); | |
// β Argument of type '"asdf"' is not assignable to parameter of type '"19000101" | ... | |
// 3οΈβ£ invalid date, February doenst have 31 days | |
const date3 = toDateString('19990231'); | |
// β Throws Error: Invalid date string: 19990231 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment