316 lines
9.3 KiB
TypeScript
Raw Normal View History

import type JSBI from 'jsbi';
import type { Temporal } from '..';
import type { BuiltinCalendarId, AnyTemporalType, CalendarSlot, TimeZoneSlot } from './internaltypes';
// Instant
export const EPOCHNANOSECONDS = 'slot-epochNanoSeconds';
// TimeZone
export const TIMEZONE_ID = 'slot-timezone-identifier';
// DateTime, Date, Time, YearMonth, MonthDay
export const ISO_YEAR = 'slot-year';
export const ISO_MONTH = 'slot-month';
export const ISO_DAY = 'slot-day';
export const ISO_HOUR = 'slot-hour';
export const ISO_MINUTE = 'slot-minute';
export const ISO_SECOND = 'slot-second';
export const ISO_MILLISECOND = 'slot-millisecond';
export const ISO_MICROSECOND = 'slot-microsecond';
export const ISO_NANOSECOND = 'slot-nanosecond';
export const CALENDAR = 'slot-calendar';
// Date, YearMonth, and MonthDay all have the same slots, disambiguation needed:
export const DATE_BRAND = 'slot-date-brand';
export const YEAR_MONTH_BRAND = 'slot-year-month-brand';
export const MONTH_DAY_BRAND = 'slot-month-day-brand';
// ZonedDateTime
export const INSTANT = 'slot-cached-instant';
export const TIME_ZONE = 'slot-time-zone';
// Duration
export const YEARS = 'slot-years';
export const MONTHS = 'slot-months';
export const WEEKS = 'slot-weeks';
export const DAYS = 'slot-days';
export const HOURS = 'slot-hours';
export const MINUTES = 'slot-minutes';
export const SECONDS = 'slot-seconds';
export const MILLISECONDS = 'slot-milliseconds';
export const MICROSECONDS = 'slot-microseconds';
export const NANOSECONDS = 'slot-nanoseconds';
// Calendar
export const CALENDAR_ID = 'slot-calendar-identifier';
interface SlotInfo<ValueType, UsedByType extends AnyTemporalType> {
value: ValueType;
usedBy: UsedByType;
}
interface SlotInfoRecord {
[k: string]: SlotInfo<unknown, AnyTemporalType>;
}
interface Slots extends SlotInfoRecord {
// Instant
[EPOCHNANOSECONDS]: SlotInfo<JSBI, Temporal.Instant | Temporal.ZonedDateTime>; // number? JSBI?
// TimeZone
[TIMEZONE_ID]: SlotInfo<string, Temporal.TimeZone>;
// DateTime, Date, Time, YearMonth, MonthDay
[ISO_YEAR]: SlotInfo<number, TypesWithCalendarUnits>;
[ISO_MONTH]: SlotInfo<number, TypesWithCalendarUnits>;
[ISO_DAY]: SlotInfo<number, TypesWithCalendarUnits>;
[ISO_HOUR]: SlotInfo<number, TypesWithCalendarUnits>;
[ISO_MINUTE]: SlotInfo<number, TypesWithCalendarUnits>;
[ISO_SECOND]: SlotInfo<number, TypesWithCalendarUnits>;
[ISO_MILLISECOND]: SlotInfo<number, TypesWithCalendarUnits>;
[ISO_MICROSECOND]: SlotInfo<number, TypesWithCalendarUnits>;
[ISO_NANOSECOND]: SlotInfo<number, TypesWithCalendarUnits>;
[CALENDAR]: SlotInfo<CalendarSlot, TypesWithCalendarUnits>;
// Date, YearMonth, MonthDay common slots
[DATE_BRAND]: SlotInfo<true, Temporal.PlainDate>;
[YEAR_MONTH_BRAND]: SlotInfo<true, Temporal.PlainYearMonth>;
[MONTH_DAY_BRAND]: SlotInfo<true, Temporal.PlainMonthDay>;
// ZonedDateTime
[INSTANT]: SlotInfo<Temporal.Instant, Temporal.ZonedDateTime>;
[TIME_ZONE]: SlotInfo<TimeZoneSlot, Temporal.ZonedDateTime>;
// Duration
[YEARS]: SlotInfo<number, Temporal.Duration>;
[MONTHS]: SlotInfo<number, Temporal.Duration>;
[WEEKS]: SlotInfo<number, Temporal.Duration>;
[DAYS]: SlotInfo<number, Temporal.Duration>;
[HOURS]: SlotInfo<number, Temporal.Duration>;
[MINUTES]: SlotInfo<number, Temporal.Duration>;
[SECONDS]: SlotInfo<number, Temporal.Duration>;
[MILLISECONDS]: SlotInfo<number, Temporal.Duration>;
[MICROSECONDS]: SlotInfo<number, Temporal.Duration>;
[NANOSECONDS]: SlotInfo<number, Temporal.Duration>;
// Calendar
[CALENDAR_ID]: SlotInfo<BuiltinCalendarId, Temporal.Calendar>;
}
type TypesWithCalendarUnits =
| Temporal.PlainDateTime
| Temporal.PlainDate
| Temporal.PlainTime
| Temporal.PlainYearMonth
| Temporal.PlainMonthDay
| Temporal.ZonedDateTime;
interface SlotsToTypes {
// Instant
[EPOCHNANOSECONDS]: Temporal.Instant;
// TimeZone
[TIMEZONE_ID]: Temporal.TimeZone;
// DateTime, Date, Time, YearMonth, MonthDay
[ISO_YEAR]: TypesWithCalendarUnits;
[ISO_MONTH]: TypesWithCalendarUnits;
[ISO_DAY]: TypesWithCalendarUnits;
[ISO_HOUR]: TypesWithCalendarUnits;
[ISO_MINUTE]: TypesWithCalendarUnits;
[ISO_SECOND]: TypesWithCalendarUnits;
[ISO_MILLISECOND]: TypesWithCalendarUnits;
[ISO_MICROSECOND]: TypesWithCalendarUnits;
[ISO_NANOSECOND]: TypesWithCalendarUnits;
[CALENDAR]: TypesWithCalendarUnits;
// Date, YearMonth, MonthDay common slots
[DATE_BRAND]: Temporal.PlainDate;
[YEAR_MONTH_BRAND]: Temporal.PlainYearMonth;
[MONTH_DAY_BRAND]: Temporal.PlainMonthDay;
// ZonedDateTime
[INSTANT]: Temporal.ZonedDateTime;
[TIME_ZONE]: Temporal.ZonedDateTime;
// Duration
[YEARS]: Temporal.Duration;
[MONTHS]: Temporal.Duration;
[WEEKS]: Temporal.Duration;
[DAYS]: Temporal.Duration;
[HOURS]: Temporal.Duration;
[MINUTES]: Temporal.Duration;
[SECONDS]: Temporal.Duration;
[MILLISECONDS]: Temporal.Duration;
[MICROSECONDS]: Temporal.Duration;
[NANOSECONDS]: Temporal.Duration;
// Calendar
[CALENDAR_ID]: Temporal.Calendar;
}
type SlotKey = keyof SlotsToTypes;
const globalSlots = new WeakMap<Slots[keyof Slots]['usedBy'], Record<keyof Slots, Slots[keyof Slots]['value']>>();
function _GetSlots(container: Slots[keyof Slots]['usedBy']) {
return globalSlots.get(container);
}
const GetSlotsSymbol = Symbol.for('@@Temporal__GetSlots');
// expose GetSlots to avoid dual package hazards
(globalThis as any)[GetSlotsSymbol] ||= _GetSlots;
const GetSlots = (globalThis as any)[GetSlotsSymbol] as typeof _GetSlots;
function _CreateSlots(container: Slots[keyof Slots]['usedBy']): void {
globalSlots.set(container, Object.create(null));
}
const CreateSlotsSymbol = Symbol.for('@@Temporal__CreateSlots');
// expose CreateSlots to avoid dual package hazards
(globalThis as any)[CreateSlotsSymbol] ||= _CreateSlots;
export const CreateSlots = (globalThis as any)[CreateSlotsSymbol] as typeof _CreateSlots;
// TODO: is there a better way than 9 overloads to make HasSlot into a type
// guard that takes a variable number of parameters?
export function HasSlot<ID1 extends SlotKey>(container: unknown, id1: ID1): container is Slots[ID1]['usedBy'];
export function HasSlot<ID1 extends SlotKey, ID2 extends SlotKey>(
container: unknown,
id1: ID1,
id2: ID2
): container is Slots[ID1]['usedBy'] | Slots[ID2]['usedBy'];
export function HasSlot<ID1 extends SlotKey, ID2 extends SlotKey, ID3 extends SlotKey>(
container: unknown,
id1: ID1,
id2: ID2,
id3: ID3
): container is Slots[ID1]['usedBy'] | Slots[ID2]['usedBy'] | Slots[ID3]['usedBy'];
export function HasSlot<ID1 extends SlotKey, ID2 extends SlotKey, ID3 extends SlotKey, ID4 extends SlotKey>(
container: unknown,
id1: ID1,
id2: ID2,
id3: ID3,
id4: ID4
): container is Slots[ID1 | ID2 | ID3 | ID4]['usedBy'];
export function HasSlot<
ID1 extends SlotKey,
ID2 extends SlotKey,
ID3 extends SlotKey,
ID4 extends SlotKey,
ID5 extends SlotKey
>(
container: unknown,
id1: ID1,
id2: ID2,
id3: ID3,
id4: ID4,
id5: ID5
): container is Slots[ID1 | ID2 | ID3 | ID4 | ID5]['usedBy'];
export function HasSlot<
ID1 extends SlotKey,
ID2 extends SlotKey,
ID3 extends SlotKey,
ID4 extends SlotKey,
ID5 extends SlotKey,
ID6 extends SlotKey
>(
container: unknown,
id1: ID1,
id2: ID2,
id3: ID3,
id4: ID4,
id5: ID5,
id6: ID6
): container is Slots[ID1 | ID2 | ID3 | ID4 | ID5 | ID6]['usedBy'];
export function HasSlot<
ID1 extends SlotKey,
ID2 extends SlotKey,
ID3 extends SlotKey,
ID4 extends SlotKey,
ID5 extends SlotKey,
ID6 extends SlotKey,
ID7 extends SlotKey
>(
container: unknown,
id1: ID1,
id2: ID2,
id3: ID3,
id4: ID4,
id5: ID5,
id6: ID6,
id7: ID7
): container is Slots[ID1 | ID2 | ID3 | ID4 | ID5 | ID6 | ID7]['usedBy'];
export function HasSlot<
ID1 extends SlotKey,
ID2 extends SlotKey,
ID3 extends SlotKey,
ID4 extends SlotKey,
ID5 extends SlotKey,
ID6 extends SlotKey,
ID7 extends SlotKey,
ID8 extends SlotKey
>(
container: unknown,
id1: ID1,
id2: ID2,
id3: ID3,
id4: ID4,
id5: ID5,
id6: ID6,
id7: ID7,
id8: ID8
): container is Slots[ID1 | ID2 | ID3 | ID4 | ID5 | ID6 | ID7 | ID8]['usedBy'];
export function HasSlot<
ID1 extends SlotKey,
ID2 extends SlotKey,
ID3 extends SlotKey,
ID4 extends SlotKey,
ID5 extends SlotKey,
ID6 extends SlotKey,
ID7 extends SlotKey,
ID8 extends SlotKey,
ID9 extends SlotKey
>(
container: unknown,
id1: ID1,
id2: ID2,
id3: ID3,
id4: ID4,
id5: ID5,
id6: ID6,
id7: ID7,
id8: ID8,
id9: ID9
): container is Slots[ID1 | ID2 | ID3 | ID4 | ID5 | ID6 | ID7 | ID8 | ID9]['usedBy'];
export function HasSlot(container: unknown, ...ids: (keyof Slots)[]): boolean {
if (!container || 'object' !== typeof container) return false;
const myslots = GetSlots(container as AnyTemporalType);
return !!myslots && ids.every((id) => id in myslots);
}
export function GetSlot<KeyT extends keyof Slots>(
container: Slots[typeof id]['usedBy'],
id: KeyT
): Slots[KeyT]['value'] {
const value = GetSlots(container)?.[id];
if (value === undefined) throw new TypeError(`Missing internal slot ${id}`);
return value;
}
export function SetSlot<KeyT extends SlotKey>(
container: Slots[KeyT]['usedBy'],
id: KeyT,
value: Slots[KeyT]['value']
): void {
const slots = GetSlots(container);
if (slots === undefined) throw new TypeError('Missing slots for the given container');
const existingSlot = slots[id];
if (existingSlot) throw new TypeError(`${id} already has set`);
slots[id] = value;
}