# 30301-IsOdd

# 题目描述

判断一个数字是否是奇数。非整数(如 2.33e23number)应返回 false

type R1 = IsOdd<1>; // true
type R2 = IsOdd<2>; // false
type R3 = IsOdd<0>; // false
type R4 = IsOdd<101>; // true
type R5 = IsOdd<-3>; // true
type R6 = IsOdd<2.3>; // false
type R7 = IsOdd<number>; // false

# 分析

最直观的思路是用元组长度累减 2:

type IsOdd<N, T = BuildTuple<N>> = T extends [any, any, ...infer R]
  ? IsOdd<never, R>
  : T extends [any]
  ? true
  : false;

但对大数字(比如 101)会爆栈,对小数和科学计数法也无从下手。

最简方案:直接看十进制末位数字。但要先排除两种"不算奇数"的情况:

  1. 非整数(2.33e23 这类带小数点或科学计数法的);
  2. 非字面量 number(没法断言末位)。

${bigint} 模板类型可以一次性卡掉这两种:它只匹配像 "-123" / "456" 这种纯整数字符串。

然后再看是否以奇数数字结尾。

# 题解

type IsOdd<N extends number> = `${N}` extends `${bigint}`
  ? `${N}` extends `${string}${'1' | '3' | '5' | '7' | '9'}`
    ? true
    : false
  : false;

两层判断:

  1. `${N}` extends `${bigint}` 确保 N 能被写成一个整数字面量,过滤掉 2.33e23number
  2. `${string}${'1'|...|'9'}` 判断末位是奇数,负号前缀不影响这个尾部匹配。

# 验证

type R1 = IsOdd<1>; // true
type R2 = IsOdd<2>; // false
type R3 = IsOdd<0>; // false
type R4 = IsOdd<101>; // true
type R5 = IsOdd<-3>; // true  (负号前缀不影响末位匹配)
type R6 = IsOdd<2.3>; // false (被 `${bigint}` 过滤掉)
type R7 = IsOdd<3e23>; // false (`"3e+23"` 不是 bigint 形状)
type R8 = IsOdd<number>; // false

# 知识点

  • ${N} 把数字转成字符串字面量,配合模板匹配是处理"大数字"题最省力的方式,见 字面量类型和基础类型
  • 避开递归深度限制的有效办法:能走字符串就走字符串。
Last Updated: 2026/4/19 01:03:18