# 30301-IsOdd
# 题目描述
判断一个数字是否是奇数。非整数(如 2.3、3e23、number)应返回 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)会爆栈,对小数和科学计数法也无从下手。
最简方案:直接看十进制末位数字。但要先排除两种"不算奇数"的情况:
- 非整数(
2.3、3e23这类带小数点或科学计数法的); - 非字面量
number(没法断言末位)。
用 ${bigint} 模板类型可以一次性卡掉这两种:它只匹配像 "-123" / "456" 这种纯整数字符串。
然后再看是否以奇数数字结尾。
# 题解
type IsOdd<N extends number> = `${N}` extends `${bigint}`
? `${N}` extends `${string}${'1' | '3' | '5' | '7' | '9'}`
? true
: false
: false;
两层判断:
`${N}` extends `${bigint}`确保 N 能被写成一个整数字面量,过滤掉2.3、3e23、number。`${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}把数字转成字符串字面量,配合模板匹配是处理"大数字"题最省力的方式,见 字面量类型和基础类型。- 避开递归深度限制的有效办法:能走字符串就走字符串。