Yuzhe's Blog

yuzhes

Absolute

Absolute

Challenge Link

Challenge

Implement the Absolute type. A type that takes a string, number, or bigint. The output should be a positive number string.

type Test = -100
type Result = Absolute<Test> // expected to be "100"

Solution

type Absolute<T extends number | string | bigint> =
  `${T}` extends `-${infer N}` ? N : `${T}`

The entire trick fits in one line: convert to a string, then strip a leading minus sign if present.

Step-by-step breakdown

Step 1 — Coerce everything to a string with a template literal

`${T}`

TypeScript’s template literal types can interpolate number, string, and bigint directly, converting their literal types into string literal types:

type A = `${-100}`    // "-100"
type B = `${100}`     // "100"
type C = `${-42n}`    // "-42"
type D = `${"-100"}`  // "-100"

This single coercion handles all three accepted input types uniformly.

Step 2 — Pattern-match against a leading minus

`${T}` extends `-${infer N}` ? N : `${T}`

All test cases walk-through

Input\${T}“Matches -${N}?Result
-100"-100"N = "100""100"
100"100""100"
"-100""-100"N = "100""100"
"100""100""100"
-42n"-42"N = "42""42"
0"0""0"

Deep Dive

Why does the output type have to be a string?

You might expect Absolute<-100> to return the number 100, not the string "100". The reason is TypeScript’s type system: while you can do arithmetic on numeric literal types in limited ways (e.g., tuple length tricks), stripping a sign character is inherently a string operation. The template literal infer pattern \-${infer N}“ can only give back a string.

If you truly needed a numeric return type you’d have to convert the resulting string back to a number, which requires the tuple-length trick and breaks for large numbers — not worth the complexity here.

Template literals as a string processing tool

This challenge demonstrates that template literal types are more than just string concatenation — they’re a lightweight string parsing mechanism:

// Extract path segments
type Head<S extends string> = S extends `${infer H}/${string}` ? H : S

// Check prefix
type StartsWithA<S extends string> = S extends `A${string}` ? true : false

// Strip leading character
type StripMinus<S extends string> = S extends `-${infer R}` ? R : S

The infer keyword inside a template literal pattern is the key enabler.

Constraint: T extends number | string | bigint

Why include bigint? Because JavaScript’s BigInt literals (like -42n) are their own primitive type in TypeScript. Without the bigint branch, you’d get a type error if someone passed a bigint. Template literal interpolation supports all three, so the constraint matches the capability.

What about "-0"?

Negative zero (-0) is a quirky JavaScript value. In TypeScript’s type system, -0 as a numeric literal is typically treated as 0, so Absolute<-0> gives "0" — which is correct.

Why not use Math.abs-style conditional on number sign?

You could try a numeric conditional approach using conditional distribution, but TypeScript’s numeric type arithmetic is very limited. You can’t write T > 0 ? T : -T at the type level for arbitrary numbers. The string-based approach is the only clean solution.

Key Takeaways

中文解析

类型定义解读

type Absolute<T extends number | string | bigint> =
  `${T}` extends `-${infer N}`  // 先用模板字面量把 T 统一转为字符串,再匹配前导负号
    ? N                          // 有负号 → 返回负号后面的部分(即绝对值字符串)
    : `${T}`                     // 无负号 → 原值即为绝对值,直接返回字符串形式

逐步分析

Step 1:模板字面量类型的强制字符串转换

`${-100}`   // "-100"(string literal type)
`${100}`    // "100"
`${-42n}`   // "-42"
`${"-100"}` // "-100"(string 原样保留)

TypeScript 的模板字面量类型可以内插 numberstringbigint,统一产出字符串字面量类型。这一步让三种不同的输入类型走同一条处理路径。

Step 2:用 infer 模式匹配剥离负号

"-100" extends `-${infer N}` → N = "100"
"100"  extends `-${infer N}` → 不匹配    ❌ → 原值返回

-${infer N} 是”以 - 开头的字符串”的类型模式,infer N 捕获减号之后的所有字符。

各测试用例走查

输入\${T}“匹配负号?结果
-100"-100"✅ N=“100”"100"
100"100""100"
"-100""-100"✅ N=“100”"100"
"100""100""100"
-42n"-42"✅ N=“42”"42"
0"0""0"

为什么输出是字符串而不是数字?

TypeScript 的类型系统不支持任意数值的算术操作(无法写 -TMath.abs<T>)。
符号剥离是字符串操作,infer 捕获的结果天然是字符串类型。
如果要转回数字类型,需要借助”元组长度技巧”,对大数无效且极其复杂——不如就接受字符串输出。

考察知识点