type CreateUnion<
N extends number,
T extends any[] = [any],
S = `${T['length']}` extends `${number}${number}` ? `${T['length']}` : `0${T['length']}`
> = N extends T['length'] ? S : S | CreateUnion<N, [...T, any]>
type ValidDate<T extends string> = T extends `${infer A}${infer B}${infer C}${infer D}`
? `${A}${B}` extends CreateUnion<12>
? `${A}${B}` extends '02' ? `${C}${D}` extends CreateUnion<28> ? true : false
: `${A}${B}` extends '01' | '03' | '05' | '07' | '08' | '10' | '12' ? `${C}${D}` extends CreateUnion<31> ? true : false
: `${C}${D}` extends CreateUnion<30> ? true : false
: false
: false
Solution by 2083335157 #35084
type AddZeroPadding<S extends string> = S extends `${1 | 2 | 3}${number}` ? S : `0${S}`;
type Range<
End extends number,
Result extends string = never,
Counter extends unknown[] = [unknown]
> = Counter["length"] extends End
? Result | AddZeroPadding<`${Counter["length"]}`>
: Range<End, Result | AddZeroPadding<`${Counter["length"]}`>, [...Counter, unknown]>;
type ValidDate<T extends string> = T extends
| `${"01" | "03" | "05" | "07" | "08" | "11" | "12"}${Range<31>}`
| `${"04" | "06" | "09" | "11"}${Range<30>}`
| `02${Range<28>}`
? true
: false;
Solution by yukicountry #34454
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
// æ°åįčåī
type Num = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
type D = 0 | 1 | 2
// æå°
type MinMM = `0${4 | 6 | 7}` | `11`
// æåĪ§
type MaxMM = Exclude<`0${Num}` | `1${D}`, MinMM | `02`>
type Day = `${D}${Num}` | `3${Exclude<D, 2>}`
type ValidDate<T extends string> = T extends `${MaxMM}${Day}` | `${MinMM}${Exclude<Day, `31`>}` | `02${Exclude<Day, `29` | `30` | `31`>}` ? true : false
Solution by wuxin0011 #27378
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.
// ============= 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;
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${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
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}`
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