04037-hard-ispalindrome

Back

type IsPalindrome<T extends number | string> =
  `${T}` extends `${infer R}${infer Tail}`
    ? Tail extends ""
      ? true
      : `${T}` extends `${R}${infer Mid}${R}`
      ? IsPalindrome<Mid>
      : false
    : true;

Solution by Vampirelee #32643

type IsPalindrome<T extends number | string> =
  `${T}` extends `${infer L}${infer R}`
    ? R extends `${infer S}${L}`
      ? S extends '' ? true : IsPalindrome<S>
      : R extends '' ? true : false
    : never

Solution by vangie #32257

type Str2Arr<T> = T extends `${infer A}${infer B}` ? [A, ...Str2Arr<B>] : []
type IsPalindrome<T extends string | number, O = Str2Arr<`${T}`>> = O extends [infer A, ...infer B, infer C] ? A extends C ? IsPalindrome<T, B> : false : true

Solution by dreamluo-plus #30776

// your answers

type IsPalindromeAry<Ary> = 
Ary extends unknown[] ?
Ary extends [] 
  ? true 
  : Ary['length'] extends 1 
    ? true 
    : Ary extends [infer Head, ...infer Mid, infer Last] ? Head extends Last ? IsPalindromeAry<Mid> : false : false: false

Solution by 437204933 #29708

type StrToArr<T extends string> = T extends `${infer Item}${infer R}`
  ? [Item, ...StrToArr<R>]
  : [];

type IsPalindrome<T extends number | string, U = StrToArr<`${T}`>> = U extends [
  infer L,
  ...infer Rest,
  infer R
]
  ? L extends R
    ? IsPalindrome<T, Rest>
    : false
  : true;

Solution by DoubleWoodLin #28944

type IsPalindrome<T extends string | number> = `${T}` extends ''
  ? true
  :  `${T}` extends `${infer First}${infer Rest}`
    ? Rest extends ''
     ? true
      : Rest extends `${infer Middle}${First}`
        ? IsPalindrome<Middle>
        : false
    : false

Solution by aybykovskii #28917

type Reverse<T extends string | number | bigint, _R extends string = ''> = 
  `${T}` extends `${infer First}${infer Rest}` ? Reverse<Rest, `${First}${_R}`> : _R

type IsPalindrome<T extends string | number> = `${T}` extends Reverse<T> ? true : false

Solution by jjswifty #27719

type IsPalindrome<S extends string | number> = `${S}` extends `${infer S0}${infer SRest}` ? (
  SRest extends `${infer SBody}${S0}` | '' ? IsPalindrome<SBody> : false
) : true

Solution by BOCbMOU #27003

Supports mixed case phrases like "Mr. Owl ate my metal worm!"

type LowerCaseAlphabet =
  | "a"
  | "b"
  | "c"
  | "d"
  | "e"
  | "f"
  | "g"
  | "h"
  | "i"
  | "j"
  | "k"
  | "l"
  | "m"
  | "n"
  | "o"
  | "p"
  | "q"
  | "r"
  | "s"
  | "t"
  | "u"
  | "v"
  | "w"
  | "x"
  | "y"
  | "z";

type Alphabet = LowerCaseAlphabet | Uppercase<LowerCaseAlphabet>;

type OnlyAlpha<
  T,
  R extends string = ""
> = T extends `${infer Char}${infer Rest}`
  ? Char extends Alphabet
    ? OnlyAlpha<Rest, `${R}${Char}`>
    : OnlyAlpha<Rest, R>
  : T extends Alphabet
  ? `${R}${T}`
  : R;

type StringToTuple<
  T,
  R extends Array<string> = []
> = T extends `${infer Char}${infer Rest}`
  ? StringToTuple<Rest, [...R, Char]>
  : R;

type TupleToString<T, R extends string = ""> = T extends [
  infer First extends string,
  ...infer Rest
]
  ? TupleToString<Rest, `${R}${First}`>
  : R;

type IsPalindrome<T extends string> = StringToTuple<
  OnlyAlpha<Lowercase<T>>
> extends [infer First, ...infer Middle, infer End]
  ? First extends End
    ? IsPalindrome<TupleToString<Middle>>
    : false
  : true;

/* _____________ Test Cases _____________ */
import type { Equal, Expect } from "@type-challenges/utils";

type cases = [
  // @ts-expect-error
  IsPalindrome<unknown>,
  // @ts-expect-error
  IsPalindrome<1>,
  Expect<Equal<IsPalindrome<"a">, true>>,
  Expect<Equal<IsPalindrome<"racecar">, true>>,
  Expect<Equal<IsPalindrome<"abca">, false>>,
  Expect<Equal<IsPalindrome<"deed">, true>>,
  Expect<Equal<IsPalindrome<"hello world">, false>>,
  Expect<Equal<IsPalindrome<"today">, false>>,
  Expect<Equal<IsPalindrome<"Mr. Owl ate my metal worm!">, true>>,
  Expect<Equal<IsPalindrome<"Do geese see God?">, true>>
];

Solution by cdierkens #26954

4037 - IsPalindrome

This challenge is just pure fun. It's not terribly difficult (despite being in the hard difficulty group) and it has a lot of interesting solutions: one of which that is guaranteed to make you smile. Who'da thunk checking numbers and strings for being palindromes would be so fun?

🎥 Video Explanation

Release Date: 2023-04-26 19:00 UTC

IsPalindrome

🔢 Code

// ============= Test Cases =============
import type { Equal, Expect } from './test-utils'

type A1 = IsPalindrome<'abc'>;
type B1 = false;
type C1 = Expect<Equal<A1, B1>>;

type A2 = IsPalindrome<'b'>;
type B2 = true;
type C2 = Expect<Equal<A2, B2>>;

type A3 = IsPalindrome<'abca'>;
type B3 = false;
type C3 = Expect<Equal<A3, B3>>;

type A4 = IsPalindrome<'abcba'>;
type B4 = true;
type C4 = Expect<Equal<A4, B4>>;

type A5 = IsPalindrome<121>;
type B5 = true;
type C5 = Expect<Equal<A5, B5>>;

type A6 = IsPalindrome<19260817>;
type B6 = false;
type C6 = Expect<Equal<A6, B6>>;

// // ============= Your Code Here =============
// type IsPalindrome<
//   T extends string | number,
//   K = `${T}`,
// > =
//   K extends `${infer Head}${infer Tail}`
//   ? Tail extends ''
//     ? true
//     : K extends `${Head}${infer Middle}${Head}`
//       ? IsPalindrome<Middle>
//       : false
//   : true;

// // ============== Alternative ==============
// type Reverse<T, Acc extends string = ''> =
//   T extends `${infer Head}${infer Tail}`
//   ? Reverse<Tail, `${Head}${Acc}`>
//   : Acc;

// type IsPalindrome<T extends string | number> =
//   `${T}` extends Reverse<`${T}`>
//   ? true
//   : false;

// ============== Alternative ==============
type StringToTuple<T extends string> =
  T extends `${infer Head}${infer Tail}`
  ? [Head, ...StringToTuple<Tail>]
  : [];

type IsIsPalindromeArray<T extends any[]> =
  T extends [infer Head, ...infer Middle, infer Tail]
  ? Head extends Tail
    ? IsIsPalindromeArray<Middle>
    : false
  : true;

type IsPalindrome<T extends string | number> =
  IsIsPalindromeArray<
    StringToTuple<`${T}`>
  >;

➕ 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 #25354

//反转法
type Reverse<T extends string, _Result extends string = ``/*Too Deep Soultion*/> = T extends `${infer F}${infer R}` ? Reverse<R, `${F}${_Result}`> : _Result;    //12345 ==> 54321
type IsPalindrome<T extends string | number> = `${T}` extends Reverse<`${T}`> ? true : false;

//靠拢法
// type ToArray<T extends string, _Result extends string[] = []/*Too Deep Soultion*/> = T extends `${infer F}${infer R}` ? ToArray<R, [..._Result, F]> : _Result;
// type DrawClose<T extends any[]> = T extends [infer F, ...infer R, infer L] ? (F extends L ? DrawClose<R> : false) : true;      //[a,bcb,a] ==> [b,c,b] ==> [c] ==> [] ==> true
// type IsPalindrome<T extends string | number> = DrawClose<ToArray<`${T}`>>;

Solution by E-uler #24980

type ReverseString<T extends string | number> = `${T}` extends `${infer First}${infer Rest}`
? `${ReverseString<Rest>}${First}`
: T

type IsPalindrome<T extends string | number> = `${T}` extends ReverseString<T> ? true : false

Solution by NeylonR #24529

// your answers
type IsPalindrome<T extends number | string,Z extends string = '',L extends string = ''> = 
`${T}` extends `${infer F}${infer R}` ? IsPalindrome<R, `${Z}${F}`, `${F}${L}`> : Equal<Z, L>

Solution by snakeUni #23974

// your answers
type ReverseStr<Str extends string> = Str extends `${infer First}${infer Rest}` ? `${ReverseStr<Rest>}${First}` : '';
type IsPalindrome<T extends number | string> = `${T}` extends ReverseStr<`${T}`> ? true : false;

Solution by jxhhdx #23663

type ToPalindrome<T extends string | number> = 
(`${T}` extends `${infer F}${infer R}` ? `${ToPalindrome<R>}${F}` : `${T}`)

type IsPalindrome<T extends string | number> = ToPalindrome<T> extends (`${T}`) ? true : false

It Also Works

type IsPalindrome<T extends string | number, Res extends string | number = T, Result extends string | number = ''> = 
`${Res}` extends `${infer F}${infer R}` 
? IsPalindrome<T, R, `${F}${Result}`>
: Result extends `${T}` ? true : false

Solution by TKBnice #23369

type IsPalindrome<T extends string | number> =
  `${T}` extends `${infer A}${infer B}${infer C}`
  ? `${B}${C}` extends `${infer D}${A}`
  ? IsPalindrome<D>
  : false
  : true
  

Solution by liuseen-l #23336

type IsPalindrome<T extends string | number> =
  `${T}` extends `${infer A}${infer B}${infer R}`
    ? `${B}${R}` extends `${infer L}${A}`
      ? IsPalindrome<L>
      : false
    : true

Solution by drylint #22758

type NumberToString = ${T} type ReverseString<T extends string, Result extends string = ''> = T extends '' ? Result : T extends ${infer F}${infer Rest} ? ReverseString<Rest, ${F}${Result}> : never

type IsPalindrome<T extends string | number> = T extends number ? NumberToString extends ReverseString<NumberToString> ? true :false : T extends ReverseString<(T extends string ? T : never)> ? true : false

Solution by amcguire704 #22491

// infer 2 characters to guarantee that the string or number has at least 2 characters
type IsPalindrome<T extends string | number> = `${T}` extends `${infer Outer}${infer Inner}${infer Right}`
// if the 2nd character with the right side end on the first character from before call this type with the inner part again
? `${Inner}${Right}` extends `${infer Rest}${Outer}` ? IsPalindrome<Rest> :
// if there are less then 2 characters left return true
false : true;

Solution by Karamuto #22214

// infer 2 characters to guarantee that the string or number has at least 2 characters
// if the 2nd character with the right side end on the first character from before call this type with the inner part again
// if there are less then 2 characters left return true
type IsPalindrome<T extends string | number> = `${T}` extends `${infer Outer}${infer Inner}${infer Right}`
? `${Inner}${Right}` extends `${infer Rest}${Outer}` ? IsPalindrome<Rest> :
false : true;

Solution by Karamuto #22213

type Reverse<T> = T extends `${infer A}${infer B}` ? `${Reverse<B>}${A}` : T
type IsPalindrome<T extends string | number>
    = `${T}` extends infer S ? Equal<S, Reverse<S>> : 'ahhhhhh~~~'

Solution by goddnsgit #21927

type Reverse<T extends string> = 
  T extends `${infer First}${infer Rest}`
  ? `${Reverse<Rest>}${First}`
  : T

type IsPalindrome<T extends string | number> = 
  `${T}` extends Reverse<`${T}`>
  ? true
  : false

playground

Solution by zhaoyao91 #21846

// your answers
//判断首元素和末元素是否相等,
//是则把true就添加进一个数组,并移除首元素和末元素,继续递归判断。
//是若是否则返回false。
//we can compare the first element with the last element ,if they are equal , we can push a true type in a array , and remove
//the first element and the last element , The next step is to proceed with recursive judgment.
//if the first element with the last element are not equal , return false directly.

type GetLastStr<T> = T extends `${infer F}${infer R}` ? R extends '' ? F : GetLastStr<R> : ''

type RemoveLastStr<T,S extends string = ''> = T extends `${infer F}${infer R}` ? 
R extends '' ? S : RemoveLastStr<R,`${S}${F}`> : ''

type IsBothTrue<T> = T extends true[] ? true : false

type NumberToStr<N extends number | string> = `${N}` 

type IsPalindrome<T extends number | string,Arr extends boolean[] = []> = 
NumberToStr<T> extends `${infer F}${infer R}` ? R extends '' ? IsBothTrue<Arr> : 
F extends GetLastStr<R> ? IsPalindrome<`${RemoveLastStr<R>}`,Arr extends [...infer Rest] ? [...Rest,true] : []> : false
: true

Solution by YqxLzx #21600

// your answers
// 1. 字符串不能够取首尾值,需要转换成数组
type ToArr<S extends string> = S extends `${infer Head}${infer Rest}` ? [Head, ...ToArr<Rest>] : []

// - 完全遍历完,再进行计算两个 res Arr 是否相等
// type IsPalindrome<T, HeadArr extends any[] = [], EndArr extends any[] = []> = T extends string | number ? IsPalindrome<ToArr<`${T}`>> : T extends [infer Head, ...infer Rest , infer End] ? IsPalindrome<Rest,[...HeadArr,Head], [...EndArr,End]> : HeadArr extends EndArr ? true : false

// -  遍历中计算是否相等
type IsPalindrome<T> = T extends string | number ? IsPalindrome<ToArr<`${T}`>> : T extends [infer Head, ...infer Rest , infer End] ? Head extends End ? IsPalindrome<Rest>: false : true
type res = [1] extends [infer Head, ...infer Rest, infer End] ? [Head, Rest, End]: false

Solution by Rebornjiang #21397

type Join<T extends Array<string>, U extends string | number> = T extends [
  infer H extends string,
  ...infer L extends string[]
]
  ? `${H}${L extends [] ? "" : U}${Join<L, U>}`
  : "";
type stringToArray<
  T extends string | number,
  Result extends Array<string> = []
> = `${T}` extends `${infer A}${infer B}`
  ? stringToArray<B, [...Result, A]>
  : Result;

type IsPalindrome<T extends string | number> = stringToArray<T> extends [
  infer A,
  ...infer B extends Array<string>,
  infer C
]
  ? A extends C
    ? IsPalindrome<Join<B, "">>
    : false
  : true;

Solution by so11y #21252

// version 1
type ToTuple<T extends number | string> = `${T}` extends `${infer F}${infer O}`
  ? [F, ...ToTuple<O>]
  : []

type IsPalindrome<T extends string | number, U = ToTuple<T>> =
  U extends [infer L, ...infer M, infer R]
    ? L extends R
      ? IsPalindrome<T, M>
      : false
    : U extends [infer L] ? true : false



// version 2
type Reverse<T extends number | string> = `${T}` extends `${infer F}${infer O}`
  ? `${Reverse<O>}${F}`
  : T

type IsPalindrome<T extends string | number> = `${T}` extends Reverse<T>
  ? true
  : false

Solution by pengzhanbo #20480

Mine.

type Reverse<T extends string | number, _Result extends string = ''> =
  `${ T }` extends `${ infer F }${ infer Rest }`
    ? Reverse<Rest, `${ F }${ _Result }`>
    : _Result

type IsPalindrome<T extends string | number> = Reverse<T> extends `${ T }` ? true : false

#16957 better.

Solution by lvjiaxuan #20301

type ToArray<S extends string | number, Result extends string[] = []> = `${S}` extends `${infer F}${infer R}` ? ToArray<R, [...Result, F]> : Result;

type ArrayToReversalString<T extends string[], Total extends string = ''> = 
T extends [...infer F extends string[], infer R extends string]
 ? ArrayToReversalString<F, `${Total}${R}`> 
 : Total;

type IsPalindrome<T extends string | number> = Equal<`${T}`, ArrayToReversalString<ToArray<T>>>;

Solution by CaoXueLiang #19482

// your answers
type StringOf<T extends string|number> = `${T}`;
type FirstChar<T extends string> = 
  T extends `${infer TFirst}${string}` ? TFirst : undefined;
type RestOfString<T extends string> = 
  T extends `${string}${infer TRest}` ? TRest : never;
type SplitString<T extends string> = [
  ...(FirstChar<T> extends string ? [FirstChar<T>] : []), 
  ...(FirstChar<T> extends string ? SplitString<RestOfString<T>> : [])
];
type Equals<T, K> = T extends K ? K extends T ? true : false : false;
type LastChar<T extends string[]> = T extends [...any[], infer TLast] ? TLast : never;
type MiddleChars<T extends string[]> = T extends [any, ...infer TMiddle extends string[], any] ? TMiddle : never;

type IsArrayPalindrome<TArray extends string[]> = 
  TArray["length"] extends 0 ? true :
  TArray["length"] extends 1 ? true :
    Equals<TArray[0], LastChar<TArray>> extends false ? false :
    IsArrayPalindrome<MiddleChars<TArray>>;
type IsPalindrome<T extends string | number> = 
  SplitString<StringOf<T>> extends infer TArray extends string[] ?
  IsArrayPalindrome<TArray> : false;

Solution by mykeels #18948

// your answers
type FirstOfStr<T extends string | number> = T extends `${infer R}${infer _U}` ? R : never;

type EndOfStr<T extends string | number> = T extends `${infer _R}${infer M}${infer L}` 
                                                ? L extends '' ? M : EndOfStr<`${M}${L}`> : never;

type GetStrLen<T extends string, Result extends unknown[] = []> = T extends `${infer _R}${infer U}` ? GetStrLen<U, [...Result, unknown]> : Result['length'];

type RemoveEndOfStr<T extends string, Result extends string = ''> = GetStrLen<T> extends 1 ? Result : T extends `${infer R}${infer U}` ? RemoveEndOfStr<U, `${Result}${R}`> : never;

type IsPalindrome<T extends string | number> = `${T}` extends ''
                                                    ? true
                                                    : GetStrLen<`${T}`> extends 1
                                                                            ? true
                                                                            : FirstOfStr<`${T}`> extends EndOfStr<`${T}`>
                                                                                                ? `${T}` extends `${infer _R}${infer P}`
                                                                                                    ? IsPalindrome<RemoveEndOfStr<P>>
                                                                                                    : T
                                                                                                : false;

Solution by jiaaoMario #18600