-
-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Open
Labels
9155answerShare answers/solutions to a questionShare answers/solutions to a questionenin Englishin English
Description
9155 - ValidDate
I found this challenge to be quite fun. It's a problem that has a few vastly different ways to approach it, which ends up being a good lesson on how to balance keeping things terse and making it easy to read. It's fun to see what TypeScript can do! Even parsing dates.. hah.
🎥 Video Explanation
🔢 Code
// ============= Test Cases =============
import type { Equal, Expect } from './test-utils'
type A1 = ValidDate<'0102'>;
type B1 = true;
type C1 = Expect<Equal<A1, B1>>;
type A2 = ValidDate<'0131'>;
type B2 = true;
type C2 = Expect<Equal<A2, B2>>;
type A3 = ValidDate<'1231'>;
type B3 = true;
type C3 = Expect<Equal<A3, B3>>;
type A4 = ValidDate<'0229'>;
type B4 = false;
type C4 = Expect<Equal<A4, B4>>;
type A5 = ValidDate<'0100'>;
type B5 = false;
type C5 = Expect<Equal<A5, B5>>;
type A6 = ValidDate<'0132'>;
type B6 = false;
type C6 = Expect<Equal<A6, B6>>;
type A7 = ValidDate<'1301'>;
type B7 = false;
type C7 = Expect<Equal<A7, B7>>;
type A8 = ValidDate<'0123'>;
type B8 = true;
type C8 = Expect<Equal<A8, B8>>;
type A9 = ValidDate<'01234'>;
type B9 = false;
type C9 = Expect<Equal<A9, B9>>;
type A10 = ValidDate<''>;
type B10 = false;
type C10 = Expect<Equal<A10, B10>>;
// ============= Your Code Here =============
type Day = {
'01': '31';
'02': '28';
'03': '31';
'04': '30';
'05': '31';
'06': '30';
'07': '31';
'08': '31';
'09': '30';
'10': '31';
'11': '30';
'12': '31';
};
type Month = keyof Day;
type RemoveZero<T> =
T extends `0${infer R}`
? RemoveZero<R>
: T;
type CheckDay<
Day extends string,
DaysThisMonth extends string,
DayWithoutZero extends string = RemoveZero<Day>,
Count extends 1[] = []
> =
DayWithoutZero extends ''
? false
: `${Count['length']}` extends DayWithoutZero
? true
: `${Count['length']}` extends DaysThisMonth
? false
: CheckDay<
Day,
DaysThisMonth,
DayWithoutZero,
[...Count, 1]
>;
type ValidDate<T extends string> =
T extends `${infer M1}${infer M2}${infer Tail}`
? `${M1}${M2}` extends Month
? CheckDay<Tail, Day[`${M1}${M2}`]>
: false
: false;
// ============== Alternatives ==============
// @teamchong
type _1_9 = 1|2|3|4|5|6|7|8|9;
type _0_9 = 0|1|2|3|4|5|6|7|8|9;
type _0_8 = 0|1|2|3|4|5|6|7|8;
type D30 = `0${_1_9}`|`1${_0_9}`|`2${_0_9}`|`30`;
type D31 = `0${_1_9}`|`1${_0_9}`|`2${_0_9}`|`30`|`31`;
type D28 = `0${_1_9}`|`1${_0_9}`|`2${_0_8}`;
type M31 = `${`01`|`03`|`05`|`07`|`08`|`10`|`12`}${D31}`;
type M30 = `${`04`|`06`|`09`|`11`}${D30}`;
type M28 = `02${D28}`;
type ValidDate<T> =
T extends M28 | M30 | M31
? true
: false;
// @LoTwT
type Num = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
type MM = `0${Num}` | `1${0 | 1 | 2}`;
type AllDate =
// All months have 0-9 days
| `${MM}${`${0}${Num}`
// All months have 10-19 days
| `${1}${0 | Num}`
// February
| `2${0 | Exclude<Num, 9>}`}`
// Non-February months ending with 30 days
| `${Exclude<MM, '02'>}${29 | 30}`
// Add the 31th days for those months that have it
| `${Exclude<MM, '02' | '04' | '06' | '09' | '11'>}${31}`;
type ValidDate<T> =
T extends AllDate
? true
: false;
// @jiangshanmeta
type _1_9 = '1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9';
type _0_9 = '0'|_1_9;
type Thirty =
| `0${_1_9}`
| `1${_0_9}`
| `2${_0_9}`
| '30';
type ThirtyOne = Thirty | '31';
type TwentyEight = Exclude<Thirty,'30' | '29'>;
type DateMap = {
'01':ThirtyOne;
'02':TwentyEight;
'03':ThirtyOne;
'04':Thirty;
'05':ThirtyOne;
'06':Thirty;
'07':ThirtyOne;
'08':ThirtyOne;
'09':Thirty;
'10':ThirtyOne;
'11':Thirty;
'12':ThirtyOne;
};
type ValidDate<T extends string> =
T extends `${infer F}${infer S}${infer Tail}`
? `${F}${S}` extends keyof DateMap
? Tail extends DateMap[`${F}${S}`]
? true
: false
: false
: false;➕ More Solutions
For more video solutions to other challenges: see the umbrella list! #21338
Metadata
Metadata
Assignees
Labels
9155answerShare answers/solutions to a questionShare answers/solutions to a questionenin Englishin English