Skip to content

Instantly share code, notes, and snippets.

@aperkaz
Last active March 1, 2024 23:03
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save aperkaz/580e72b98eba5afac30549387562655d to your computer and use it in GitHub Desktop.
Save aperkaz/580e72b98eba5afac30549387562655d to your computer and use it in GitHub Desktop.
Typing date strings in TypeScript πŸš€
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