Item 57: Prefer Tail-Recursive Generic Types
要点
- Aim to make your recursive generic types tail recursive. They're more efficient and have greater depth limits.
- Recursive type aliases can often be made tail recursive by rewriting them to use an accumulator.
- 旨在使递归泛型类型尾递归。尾递归更加高效,且具有更大的深度限制。
- 通过重写递归类型别名,使其使用累加器,可以将其转换为尾递归形式。
正文
ts
function sum(nums: readonly number[]): number {
if (nums.length === 0) {
return 0
}
return nums[0] + sum(nums.slice(1))
}
console.log(sum([0, 1, 2, 3, 4]))
ts
const arr = Array(7875).fill(1)
console.log(sum(arr))
ts
function sum(nums: readonly number[], acc = 0): number {
if (nums.length === 0) {
return acc
}
return sum(nums.slice(1), nums[0] + acc)
}
ts
type GetChars<S extends string> =
S extends `${infer FirstChar}${infer RestOfString}`
? FirstChar | GetChars<RestOfString>
: never
type ABC = GetChars<'abc'>
// ^? type ABC = "a" | "b" | "c"
ts
type Long = GetChars<'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX'>
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Type instantiation is excessively deep and possibly infinite.
ts
type ToSnake<T extends string> = string extends T
? string // We want ToSnake<string> = string
: T extends `${infer First}${infer Rest}`
? First extends Uppercase<First> // Is First a capital letter?
? `_${Lowercase<First>}${ToSnake<Rest>}` // e.g. "B" -> "_b"
: `${First}${ToSnake<Rest>}`
: T
type S = ToSnake<'fooBarBaz'>
// ^? type S = "foo_bar_baz"
type Two = ToSnake<'className' | 'tagName'>
// ^? type Two = "class_name" | "tag_name"
ts
type Long = ToSnake<'reallyDescriptiveNamePropThatsALittleTooLoquacious'>
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Type instantiation is excessively deep and possibly infinite.
ts
type ToSnake<T extends string, Acc extends string = ''> = string extends T
? string // We want ToSnake<string> = string
: T extends `${infer First}${infer Rest}`
? ToSnake<
Rest,
First extends Uppercase<First>
? `${Acc}_${Lowercase<First>}`
: `${Acc}${First}`
>
: Acc
type S = ToSnake<'fooBarBaz'>
// ^? type S = "foo_bar_baz"
type Two = ToSnake<'className' | 'tagName'>
// ^? type Two = "class_name" | "tag_name"
type Long = ToSnake<'reallyDescriptiveNamePropThatsALittleTooLoquacious'>
// ^? type Long = "really_descriptive_name_prop_thats_a_little_too_loquacious"