09155-hard-validdate

Back

type Ends = ['29' | '30', '29' | '30' | '31', never];
type D<E extends number> = `${0 | 1 | 2}${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8}` | '09' | '19' | Ends[E];

type All = `01${D<1>}` | `02${D<2>}` | `03${D<1>}` | `04${D<0>}` | `05${D<1>}` | `06${D<0>}` |
           `07${D<1>}` | `08${D<1>}` | `09${D<0>}` | `10${D<1>}` | `11${D<0>}` | `12${D<1>}`;

type ValidDate<T extends string> = T extends All ? true : false;

Solution by alexandroppolus #33508

type Days31 =
  | "01"
  | "02"
  | "03"
  | "04"
  | "05"
  | "06"
  | "07"
  | "08"
  | "09"
  | "10"
  | "11"
  | "12"
  | "13"
  | "14"
  | "15"
  | "16"
  | "17"
  | "18"
  | "19"
  | "20"
  | "21"
  | "22"
  | "23"
  | "24"
  | "25"
  | "26"
  | "27"
  | "28"
  | "29"
  | "30"
  | "31";

type MonthDayMap = {
  "01": Days31;
  "02": Exclude<Days31, "29" | "30" | "31">;
  "03": Days31;
  "04": Exclude<Days31, "31">;
  "05": Days31;
  "06": Exclude<Days31, "31">;
  "07": Days31;
  "08": Days31;
  "09": Exclude<Days31, "31">;
  "10": Days31;
  "11": Exclude<Days31, "31">;
  "12": Days31;
};
type ValidDate<T extends string> = T extends `${keyof MonthDayMap}${infer Day}`
  ? T extends `${infer Month}${Day}`
    ? Day extends MonthDayMap[Month & keyof MonthDayMap]
      ? true
      : false
    : false
  : false;

Solution by Vampirelee #32662

EZ.

// your answers
type Int = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '0';
type February = '02'
type MonthsHasDays30 = '04' | '06' | '09' | '11';
type MonthsHasDays31 = '01' | '03' | '05' | '07' | '08' | '10' | '12';
type Months = February | MonthsHasDays30 | MonthsHasDays31;
type Days = Exclude<`${0 | 1 | 2}${Int}` | '30' | '31', '00'>

type Decimal = `${Int}${Int}`;

type DaysByMonth<T extends February | MonthsHasDays30 | MonthsHasDays31> = 
  T extends February 
    ? Exclude<Days, '29' | '30' | '31'> 
    : T extends MonthsHasDays30 
      ? Exclude<Days, '30'>
      : Days

type AllDays = {
  [M in Months]: `${M}${DaysByMonth<M>}`
}[Months]

type ValidDate<T extends string> = T extends AllDays ? true : false;

Solution by kakasoo #32605

type GreaterThan<
  T extends number,
  U extends number,
  R extends any[] = []
> = T extends R["length"]
  ? false
  : U extends R["length"]
  ? true
  : GreaterThan<T, U, [...R, 1]>;

type toNumber<T extends string> = T extends `0${infer R}`
  ? toNumber<R>
  : T extends `${infer F extends number}`
  ? F
  : 0;

type DaysLimit<T extends number> = [never,32,29,32,31,32,31,32,32,31,32,31,32][T];

type ValidDate<T extends string> = T extends `${infer M1}${infer M2}${infer D}`
  ? [
      GreaterThan<13, toNumber<`${M1}${M2}`>>,
      GreaterThan<toNumber<D>, 0>,
      GreaterThan<DaysLimit<toNumber<`${M1}${M2}`>>, toNumber<D>>
    ] extends [true, true, true]
    ? true
    : false
  : false;

Solution by vangie #32339

type MonthValue = 0 | 1
type DecimalValue = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
type DayValue = 0 | 1 | 2 | 3

type ValidDate<
  T extends string
> = `${T}` extends `${MonthValue}${DecimalValue}${DayValue}${DecimalValue}` 
  ? `${T}` extends `00${string}` 
    | `${string}00` 
    | `1${Exclude<DecimalValue, 0 | 1 | 2>}${string}` 
    | `${string}3${Exclude<DecimalValue, 1>}` 
    | `0229` 
      ? false 
      : true 
  : false

Solution by simone-paglino #29652

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

Release Date: 2023-05-02 19:00 UTC

ValidDate

ðŸ”Ē 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! https://github.com/type-challenges/type-challenges/issues/21338

Solution by dimitropoulos #25355

type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
type Month = Exclude<Digit, 0> | 10 | 11 | 12;
type LargeMonth = Exclude<Month, 2 | 4 | 6 | 9 | 11>;
type ValidDay<Month extends number> =
  `0${Exclude<Digit, 0>}` |     //01~09
  `${1 | 2}${Month extends 2 ? Exclude<Digit, 9> : Digit}` |    //10~28 | 10~29
  `${Month extends 2 ? never : Month extends LargeMonth ? 31 : 30}`;    //never | 30 | 31
type Number<T extends string | number> = T extends `${infer F}${infer R}` ? (F extends `0` ? Number<R> : (T extends `${infer N extends number}` ? N : T)) : T;

type ValidDate<T extends string> = T extends `${infer M1 extends number}${infer M2 extends number}${infer D1 extends number}${infer D2 extends Digit}` ?
  (Number<`${M1}${M2}`> extends Month ?
    (`${D1}${D2}` extends ValidDay<Number<`${M1}${M2}`>> ? true : false)
    : false)
  : false;

Solution by E-uler #25109

type Months = '01' | '02' | '03' | '04' | '05' | '06' | '07' | '08' | '09' | '10' | '11' | '12'
type DaysFebruary = Months | '13' | '14' | '15' | '16' | '17' | '18' | '19' | '20' | '21' | '22' | '23' | '24' | '25' | '26' | '27' | '28'
type DaysFullMonth = DaysFebruary | '29' | '30' | '31'


type ValidDate<T extends string> = T extends `${infer Month extends Months}${DaysFullMonth}`
? Month extends '02' 
  ? T extends `${Months}${DaysFebruary}`
    ? true
    : false
  : true
: false

Solution by NeylonR #24536

// your answers

type Base = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

type FillZero<T extends string | number> = `${T}` extends `${Base}` ? `0${T}`: T

type toTuple<T extends number, R extends any[] = []> = R['length'] extends T ? R : toTuple<T, [...R, 1]>

type NumberRange<
  L extends number,
  H extends number,
  R extends any[] = toTuple<L>,
  Set extends any = never
> = R['length'] extends H
  ? Set | FillZero<H>
  : NumberRange<L, H, [...R, 1], Set | FillZero<R['length']>>

type Dates<> = {
  [K in NumberRange<1, 12>] : `${K}${NumberRange<1, 31>}`
}

type Special = "0229" | "0230" | `${"02" | "04" | "06" | "09" | "11"}${31}`

type ValidDate<T extends string> = T extends Exclude<Dates[keyof Dates], Special>
  ? true
  : false

Solution by jxhhdx #24513

// your answers
type DaysOfFeb = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28
type DaysOfSM = DaysOfFeb | 29 | 30
type DaysOfBM = DaysOfSM | 31

type MonthDays = {
  1: DaysOfBM,
  2: DaysOfFeb,
  3: DaysOfBM,
  4: DaysOfSM,
  5: DaysOfBM,
  6: DaysOfSM,
  7: DaysOfBM,
  8: DaysOfBM,
  9: DaysOfSM,
  10: DaysOfBM,
  11: DaysOfSM,
  12: DaysOfBM,
}

type Months = keyof MonthDays

type Format<T extends number | string> = 
  T extends unknown
  ? `${T}` extends `${1|2|3|4|5|6|7|8|9}`
    ? `0${T}`
    : `${T}`
  : never

// enum all
type ValidDates<M extends Months = Months> = 
  M extends unknown 
  ? `${Format<M>}${Format<MonthDays[M]>}`
  : never

type ValidDate<T extends string> = T extends ValidDates ? true : false

Solution by snakeUni #24067

type StringToNumber<T extends string | number> =
  `${T}` extends `0${infer R extends number}`
    ? StringToNumber<R>
    : `${T}` extends `${infer R extends number}`
      ? R
      : never

type GreaterThanOrEqual<
  T extends number,
  U extends number,
  R extends unknown[] = [],
> = T extends U
  ? true
  : R['length'] extends T
    ? false
    : R['length'] extends U
      ? true
      : GreaterThanOrEqual<T, U, [...R, 0]>

interface Days {
  '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 ValidDate<T extends string> =
  T extends `${infer M1}${infer M2}${infer D}`
    ? `${M1}${M2}` extends keyof Days
      ? GreaterThanOrEqual<StringToNumber<D>, 1>
      | GreaterThanOrEqual<Days[`${M1}${M2}`], StringToNumber<D>> extends true
        ? true
        : false
      : false
    : false

Solution by drylint #22975

type OneThroughNine = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

type GenerateLimit<Limit extends any[], Result extends string[]= [never]> = Result['length'] extends Limit['length'] ? Result[number] : Result['length'] extends OneThroughNine ? GenerateLimit<Limit, [...Result, 0${Result['length']}]> : GenerateLimit<Limit, [...Result, ${Result['length']}]>

type Days = GenerateLimit<[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31]> type Months = GenerateLimit<[0,1,2,3,4,5,6,7,8,9,10,11,12]>

type ValidDate = T extends ${Months}${Days} ? T extends 0229 ? false :true :false

Solution by amcguire704 #22490

// toolkit to build number ranges as strings
type NTuple<N extends number, C extends unknown[]=[]> = C['length'] extends N ? C : NTuple<N, [...C, 0]>;
type Inc<N extends number> = [...NTuple<N>, 0]['length'];
type Subtract<T extends number, U extends number> = [...NTuple<T>] extends [...NTuple<U>, ...infer Rest] ? Rest['length'] : never;
type PaddedNumString<N extends number> = `${N}` extends `${infer F}${infer L}` ? L extends '' ? `0${F}` : `${N}` : never; 
type NumberRangeArray<Start extends number, End extends number> = Subtract<End, Start> extends 0 ? [PaddedNumString<End>] :
[PaddedNumString<Start>, ...NumberRangeArray<Inc<Start> extends number ? Inc<Start> : never, End>];

// the possible day values
type February = NumberRangeArray<1,28>[number];
type MinDayRange = February | NumberRangeArray<29,30>[number];
type MaxDayRange = MinDayRange | '31';

// month days are weird....
type YearMap = {
  '01': MaxDayRange,
  '02': February,
  '03': MaxDayRange,
  '04': MinDayRange,
  '05': MaxDayRange,
  '06': MinDayRange,
  '07': MaxDayRange,
  '08': MaxDayRange,
  '09': MinDayRange,
  '10': MaxDayRange,
  '11': MinDayRange,
  '12': MaxDayRange
}
type GetFromMap<T> = T extends keyof YearMap ? T : never;
type ValidDate<T extends string> = T extends `${infer M1}${infer M2}${infer D}` ? 
D extends YearMap[GetFromMap<`${M1}${M2}`>] ? true : false : false;

Solution by Karamuto #22227

type DaysOfFeb = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28
type DaysOfSM = DaysOfFeb | 29 | 30
type DaysOfBM = DaysOfSM | 31

type MonthDays = {
  1: DaysOfBM,
  2: DaysOfFeb,
  3: DaysOfBM,
  4: DaysOfSM,
  5: DaysOfBM,
  6: DaysOfSM,
  7: DaysOfBM,
  8: DaysOfBM,
  9: DaysOfSM,
  10: DaysOfBM,
  11: DaysOfSM,
  12: DaysOfBM,
}

type Months = keyof MonthDays

type Format<T extends number | string> = 
  T extends unknown
  ? `${T}` extends `${1|2|3|4|5|6|7|8|9}`
    ? `0${T}`
    : `${T}`
  : never

type ValidDates<M extends Months = Months> = 
  M extends unknown 
  ? `${Format<M>}${Format<MonthDays[M]>}`
  : never

type ValidDate<T extends string> = T extends ValidDates ? true : false

playground

äļ­æ–‡įŽ”čŪ°

Solution by zhaoyao91 #22010

// your answers
type Range = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
type WithOutZeroRange = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
type FebDays = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' 

type Getlen<T extends string,S extends any[] = []> = T extends `${infer F}${infer Rest}` ? 
Getlen<Rest,S extends [...infer R] ? [...R,1] : []> : S['length']

type MonthRange<T> = T extends `${infer F}${infer S}${infer Rest}` ? 
F extends '1' ?  S extends '0' | '1' | '2' ?  true : false : 
F extends '0' ? S extends Range ? true : false :true : false

type DayRange<T> = T extends `${infer F}${infer S}${infer Rest}` ?  
F extends '0' ? S extends WithOutZeroRange ?  true : false :
F extends  '1' ?  S extends Range ?  true : false :
F extends '2' ? S extends FebDays ? true : false:
F extends '3' ? S extends '0' | '1' ?  true : false : false : false

type DateRule<T extends string> = Getlen<T> extends 4 ?  
T extends `${infer F}${infer S}${infer Third}${infer Fourth}` ? 
Equal<MonthRange<`${F}${S}`>,DayRange<`${Third}${Fourth}`>> : false : false

type ValidDate<T extends string> = DateRule<T>

Solution by YqxLzx #21908

type Build<
  T extends number,
  Result extends Array<never> = []
> = Result["length"] extends T ? Result : Build<T, [...Result, never]>;
type EGT<
  T extends number,
  B extends number
> = Build<T> extends [...Build<B>, ...infer C]
    ? true
    : false

type RLT<
  T extends number,
  B extends number
> = Build<B> extends [...Build<T>, ...infer C]
    ? true
    : false

type ConvertNumber<T extends string> = T extends `0${infer C extends number}`
  ? C
  : T extends `${infer G extends number}`
  ? G
  : never;

type ValidDate<T extends string> =
T extends `0229` ? false :
T extends `${infer A}${infer B}${infer C}${infer D}` ?
  [
    EGT<ConvertNumber<`${A}${B}`>,1>,
    RLT<ConvertNumber<`${A}${B}`>,12>,
    EGT<ConvertNumber<`${C}${D}`>,1>,
    RLT<ConvertNumber<`${C}${D}`>,31>,
  ] extends [true,true,true,true] ? true : false: false

Solution by so11y #21327

type ThirtyDaysMonth = "04" | "06" | "09" | "11";
type ThirtyOneDaysMonth = "01" | "03" | "05" | "07" | "08" | "10" | "12";
type February = "02";

type GetDay<Arr extends any[], N extends number = Arr["length"]> = N extends
  | 1
  | 2
  | 3
  | 4
  | 5
  | 6
  | 7
  | 8
  | 9
  ? `0${N}`
  : `${N}`;

type Range<N, Arr extends string[] = ["01"]> = Arr["length"] extends N
  ? Arr[number]
  : Range<N, [...Arr, GetDay<[...Arr, any]>]>;

type ValidDays<Days, Max> = Days extends Range<Max> ? true : false;

type ValidDate<T extends string> = T extends `${February}${infer days}`
  ? ValidDays<days, 28>
  : T extends `${ThirtyDaysMonth}${infer days}`
  ? ValidDays<days, 30>
  : T extends `${ThirtyOneDaysMonth}${infer days}`
  ? ValidDays<days, 31>
  : false;

Solution by bedis-elacheche #21117

type Month =
  | '01'
  | '02'
  | '03'
  | '04'
  | '05'
  | '06'
  | '07'
  | '08'
  | '09'
  | '10'
  | '11'
  | '12';

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 RemoveZero<T extends string> = T extends `0${infer R}` ? RemoveZero<R> : T;
type CheckDay<
  T extends string,
  M extends string,
  V extends string = RemoveZero<T>,
  R extends unknown[] = []
> = V extends ''
  ? false
  : `${R['length']}` extends V
  ? true
  : `${R['length']}` extends M
  ? false
  : CheckDay<T, M, V, [...R, 1]>;

type ValidDate<T extends string> = T extends `${infer M1}${infer M2}${infer R}`
  ? `${M1}${M2}` extends Month
    ? CheckDay<R, Day[`${M1}${M2}`]>
    : false
  : false;

Solution by bigcreate #20898

type Month31 = '01' | '03' | '05' | '07' | '08' | '10' | '12'
type Month30 = '04' | '06' | '09' | '11'
type Month28 = '02'

type _AllNumber = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '0'

type ValidDate2<T extends string> =
  T extends `${ Month31 }${ infer Date1 }${ infer Date2 }`
    ? Date1 extends '0'
      ? Date2 extends '0' ? false : true // == __00
      : Date1 extends '1' | '2'
        ? Date2 extends _AllNumber ? true : false
        : Date1 extends '3'
          ? Date2 extends '0' | '1'
            ? true
            : false // > __32
          : false // > 4___
    : T extends `${ Month30 }${ infer Date1 }${ infer Date2 }`
      ? Date1 extends '0'
        ? Date2 extends '0' ? false : true // == __00
        : Date1 extends '1' | '2'
          ? Date2 extends _AllNumber ? true : false
          : Date1 extends '3'
            ? Date2 extends '0'
              ? true
              : false // > __31
            : false // 4___
      : T extends `${ Month28 }${ infer Date1 }${ infer Date2 }`
        ? Date1 extends '0'
          ? Date2 extends '0' ? false : true // == __00
          : Date1 extends '1'
            ? Date2 extends _AllNumber ? true : false
            : Date1 extends '2'
              ? Date2 extends '9'
                ? false // > 0229
                : true
              : false
        : false

#16935 is better.

Solution by lvjiaxuan #20651

type Day31 = '1' | '3' | '5' | '7' | '8' | '10' | '12';

type Day30 = '4' | '6' | '9' | '11';

type Day28 = '2';

type NumberToArray<N extends string, T extends any[] = []> = `${T['length']}` extends `${N}` ? T : NumberToArray<N, [...T, 1]>;

type GetNumber<S extends string> = S extends `${infer F}${infer R}` ? (F extends '0' ? R : `${F}${R}`) : `${S}`;

type IsAvaildMonth<S extends string> = NumberToArray<'12'> extends [...NumberToArray<GetNumber<S>>, ...infer R] ? true : false;

type GetResultDays<Month extends string> = GetNumber<Month> extends Day31 ? '31' : GetNumber<Month> extends Day30 ? '30' : GetNumber<Month> extends Day28 ? '28' : never;

type IsAvaildDay<Month extends string, S extends string> = S extends '00' ? false : NumberToArray<GetResultDays<Month>> extends [...NumberToArray<GetNumber<S>>, ...infer R] ? true : false;

type ValidDate<S extends string> = S extends `${infer One}${infer Two}${infer Three}${infer Four}`
  ? IsAvaildMonth<`${One}${Two}`> extends true
    ? IsAvaildDay<`${One}${Two}`, `${Three}${Four}`> extends true
      ? true
      : false
    : false
  : false;

Solution by CaoXueLiang #19515

type M1 = 01 | 03 | 05 | 07 | 08 | 10 | 12; type M2 = 04 | 06 | 09 | 11; type M3 = 02; type AddZero<T extends number> = ${T} extends ${infer K}${infer F} ? F extends `` ? 0${K} : ${T} : ${T}; type D1<T = 28, S extends 0[] = [0], R = never> = S['length'] extends T ? R | ${AddZero<S[length]>} : D1<T, [...S, 0], R | ${AddZero<S['length']>}>; type D2 = 29 | 30; type D3 = 31;

type ValidDate<T extends string> = T extends ${M1}${D1 | D2 | D3} ? true : T extends ${M2}${D1 | D2} ? true : T extends ${M3}${D1} ? true : false;

Solution by my5201314zwl #16935

// your answers

/**
 * StringToNumber<'01'> // 1
 * StringToNumber<'1'> // 1
 * SttringToNumber<''> // 0
 */
type StringToNumber<T extends string> = T extends `0${infer R extends number}`
  ? R
  : T extends `${infer R extends number}`
    ? R
    : 0

type PlusOne<T extends number, R extends 0[] = []> = R['length'] extends T
  ? [0, ...R]['length']
  : PlusOne<T, [0, ...R]>

/**
 * GetDateAndMonth<'0'> // [0, 0]
 * GetDateAndMonth<'0123'> // [1, 23]
 * GetDateAndMonth<'01234'> // [1, 234]
 */
type GetDateAndMonth<T extends string, C extends number = 0, Date extends string = '', Month extends string = ''> = C extends 2
  ? [StringToNumber<Date>, StringToNumber<T>]
  : T extends `${infer F}${infer R}`
    ? GetDateAndMonth<R, PlusOne<C>, `${Date}${F}`>
    : [StringToNumber<Date>, StringToNumber<Month>]

type NumberToTuple<T extends number, Res extends 0[] = []> = Res['length'] extends T
  ? Res
  : NumberToTuple<T, [...Res, 0]>
  
type MinusOne<T extends number, Res extends 0[] = NumberToTuple<T>> = Res extends [infer F, ...infer R]
  ? R['length']
  : never
  
type GT<T extends number, U extends number> = T extends U
  ? true
  : T extends 0
    ? false
    : GT<MinusOne<T>, U>

/**
 * GreaterThan<1, 2> // false
 * GreaterThan<2, 2> // false
 * GreaterThan<3, 2> // true
 */
type GreaterThan<T extends number, U extends number> = Equal<T, U> extends true
  ? false
  : GT<T, U>

/**
 * InRange<2, 1, 2> // false
 * InRange<2, 1, 3> // true
 */
type InRange<A extends number, F extends number, R extends number> = GreaterThan<A, F> extends true
  ? GreaterThan<R, A> extends true
    ? true
    : false
  : false

type ValidDate<T extends string, A extends [number, number] = GetDateAndMonth<T>> = InRange<A[0], 0, 13> extends true
  ? A[0] extends 2
    ? InRange<A[1], 0, 29> extends true
      ? true
      : false
    : InRange<A[1], 0, 32> extends true
      ? true
      : false
  : false

Solution by humandetail #16511

// your answers
type DaysInMonth =
{
  '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 Dictionary =
{
  '0':[],
  '1':[0],
  '2':[0,0],
  '3':[0,0,0],
  '4':[0,0,0,0],
  '5':[0,0,0,0,0],
  '6':[0,0,0,0,0,0],
  '7':[0,0,0,0,0,0,0],
  '8':[0,0,0,0,0,0,0,0],
  '9':[0,0,0,0,0,0,0,0,0],  
} ;

type _10 =[0,0,0,0,0,0,0,0,0,0] ;

type Multyply<
    A extends readonly any[],
    B extends readonly any[]> =
    A extends [infer R,...infer U]
    ? [...B,...Multyply<U,B>]
    : []  ;

type ReverseString<
    S extends string> =
    S extends `${infer R extends string}${infer U extends string}`
    ? `${ReverseString<U>}${R}`
    : ''  ;

type ReverseStringToTuple<
    S extends string> =
    S extends `${infer R extends keyof Dictionary}${infer U extends string}`
    ? [...Dictionary[R],...Multyply<_10,ReverseStringToTuple<U>>]
    : []  ;

type ValidDate<
    S extends string> = 
    S extends `${infer R}${infer U}${infer V}`
    ? `${R}${U}` extends keyof DaysInMonth
    ? ReverseStringToTuple<ReverseString<V>> extends []
    ? false
    : ReverseStringToTuple<ReverseString<`${DaysInMonth[`${R}${U}`]}`>> extends
    [...ReverseStringToTuple<ReverseString<V>>,...0[]]
    ? true
    : false 
    : false
    : false ;

Solution by justBadProgrammer #16071

// your answers
type PlusOne<T extends number, Res extends 1[] = []> = Res['length'] extends T ? [...Res, 1]['length'] : PlusOne<T, [...Res, 1]>
type Rang<T extends number, Res extends any[] = ['00'], Flag extends boolean = Res['length'] extends 10 ? false : true> = 
  Res['length'] extends PlusOne<T> 
    ? Exclude<Res[number], '00'>
    : Flag extends true 
      ? Rang<T, [...Res, `0${Res['length']}`]>
      : Rang<T, [...Res, `${Res['length']}`], Flag>
type Date = {
  '01': Rang<31>,
  '02': Rang<28>,
  '03': Rang<31>,
  '04': Rang<30>,
  '05': Rang<31>,
  '06': Rang<30>,
  '07': Rang<31>,
  '08': Rang<31>,
  '09': Rang<30>,
  '10': Rang<31>,
  '11': Rang<30>,
  '12': Rang<31>,
}
type ValidDate<T extends string> = keyof {
  [
    P in keyof Date as 
      T extends `${P}${infer Rest}` 
        ? Rest extends Date[P] 
          ? P 
          : never 
        : never
  ]: any
} extends never ? false : true

Solution by Sliect #14667

Other solutions mainly use union types of string literals, I find it feasible to solve the problem without them:

type ValidDate<T extends string> = T extends `${infer M1}${infer M2}${infer D1}${infer D2}${infer Rest}` ? Rest extends '' ?
  `${M1}${M2}` extends keyof MonthDays ?
    `${D1}${D2}` extends '00' ? false :
      InRange<MonthDays[`${M1}${M2}`], `${D1}${D2}`>
  : false
: false : false;

type MonthDays = {
  '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 GreaterMap = {
  '0': [],
  '1': ['0'],
  '2': ['1', '0'],
  '3': ['2', '1', '0'],
  '4': ['3', '2', '1', '0'],
  '5': ['4', '3', '2', '1', '0'],
  '6': ['5', '4', '3', '2', '1', '0'],
  '7': ['6', '5', '4', '3', '2', '1', '0'],
  '8': ['7', '6', '5', '4', '3', '2', '1', '0'],
  '9': ['8', '7', '6', '5', '4', '3', '2', '1', '0'],
}

type Greater<A extends string, B extends string> = A extends keyof GreaterMap ? Contains<B, GreaterMap[A]> : never;
type Contains<B extends string, ARR extends any[]> = ARR extends [infer Head, ...infer Rest] ?
  Eq<B, Head> extends true ? true : Contains<B, Rest>
: false;
type GreaterOrEq<A extends string, B extends string> = Greater<A, B> extends true ? true : Eq<A, B>;
type Eq<A extends any, B extends any> = A extends B ? B extends A ? true : false : false;

type InRange<R extends string, T extends string> = R extends `${infer R1}${infer R2}` ?
  T extends `${infer T1}${infer T2}` ?
    Greater<R1, T1> extends true ? true : Eq<R1, T1> extends true ? GreaterOrEq<R2, T2> : false
  : never
: never;

Solution by darkyzhou #11787

type ValidDate<T extends string> = T extends M28 | M30 | M31 ? true : false

type _0_8 = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
type _0_9 = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
type _1_9 = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
type D28 = `0${_1_9}` | `1${_0_9}` | `2${_0_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 M31 = `${`01` | `03` | `05` | `07` | `08` | `10` | `12`}${D31}`
type M30 = `${`04` | `06` | `09` | `11`}${D30}`
type M28 = `02${D28}`

Playground

Solution by teamchong #11748

// your answers
type D30 =
  | `${0 | 1 | 2}${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`
  | "10"
  | "20"
  | "30";
type D31 = D30 | "31";
type D28 = Exclude<D30, "29" | "30">;

type ValidDate<T extends string> = T extends `${
  | "01"
  | "03"
  | "05"
  | "07"
  | "08"
  | "10"
  | "12"}${D31}`
  ? true
  : T extends `${"04" | "06" | "09" | "11"}${D30}`
  ? true
  : T extends `${"02"}${D28}`
  ? true
  : false;

Solution by coderhyy #11542

type D30 = `${ 0 | 1 | 2 }${ 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 }` | '20' | '30'
type D31 = D30 | '31'
type D28 = Exclude<D30, '29' | '30'>

type ValidDate<T extends string> =
  T extends `${'01' | '03' | '05' | '07' | '08' | '10' | '12'}${infer Date}`
    ? Date extends D31 ? true : false
    : T extends `${'04' | '06' | '09' | '11'}${infer Date}`
      ? Date extends D30 ? true : false
      : T extends `02${infer Date}`
        ? Date extends D28 ? true : false
        : false

Solution by Alexsey #11380

type Num = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
type Whole = Num | 0;
type Day31 = '01' | '03' | '05' | '07' | '08' | '10' | '12'
type Day30 = '04' | '06' | '09' | '11'

type ValidMonth<Month> = Month extends `0${Num}` | '10' | '11' | '12' ? true : false;

type ValidDay<Month extends string, Day extends string> = Month extends Day31
? Day extends `0${Num}` | `1${Whole}` | `2${Whole}` | '30' | '31'
  ? true
  : false
: Month extends Day30
  ? Day extends `0${Num}` | `1${Whole}` | `2${Whole}` | '30'
    ? true
    : false
  : Day extends `0${Num}` | `1${Whole}` | `2${Exclude<Whole, 9>}`
    ? true
    : false;

type ValidDate<T extends string> = `${T}` extends `${infer M1}${infer M2}${infer D1}${infer D2}`
  ? ValidMonth<`${M1}${M2}`> extends true
    ? ValidDay<`${M1}${M2}`, `${D1}${D2}`> extends true
      ? true
      : false
    : false
  : false;

Solution by Psilocine #9506

type one2nine = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
type zero2nine = '0' | one2nine;

type thirty = `0${one2nine}` | `1${zero2nine}` | `2${zero2nine}` | '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 D}`?
  `${F}${S}` extends keyof DateMap?
    D extends DateMap[`${F}${S}`]?true:false
    :false
  :never;

Solution by jiangshanmeta #9283