ts类型挑战【十八】

254 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第20天,点击查看活动详情

题目二十七:camelcase

// template.ts
type CamelCase<S> = any;
// test-cases.ts
import { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<CamelCase<'foo-bar-baz'>, 'fooBarBaz'>>,
  Expect<Equal<CamelCase<'foo-Bar-Baz'>, 'foo-Bar-Baz'>>,
  Expect<Equal<CamelCase<'foo-Bar-baz'>, 'foo-BarBaz'>>,
  Expect<Equal<CamelCase<'foo-bar'>, 'fooBar'>>,
  Expect<Equal<CamelCase<'foo_bar'>, 'foo_bar'>>,
  Expect<Equal<CamelCase<'foo--bar----baz'>, 'foo-Bar---Baz'>>,
  Expect<Equal<CamelCase<'a-b-c'>, 'aBC'>>,
  Expect<Equal<CamelCase<'a-b-c-'>, 'aBC-'>>,
  Expect<Equal<CamelCase<'ABC'>, 'ABC'>>,
  Expect<Equal<CamelCase<'-'>, '-'>>,
  Expect<Equal<CamelCase<''>, ''>>,
  Expect<Equal<CamelCase<'😎'>, '😎'>>,
]

将烤串转驼峰

测试用例

  • Equal<CamelCase<'foo-bar-baz'>, 'fooBarBaz'>

正常的将烤串语法转为驼峰语法

  • Equal<CamelCase<'foo-Bar-Baz'>, 'foo-Bar-Baz'>

如果相应需要转换的字母已经是大写了,则不进行转换

  • Equal<CamelCase<'foo_bar'>, 'foo_bar'>

只会对中划线进行转换

  • Equal<CamelCase<'foo--bar----baz'>, 'foo-Bar---Baz'>

多个中划线,只会删除一个后将其后方最近的一个字母转为大写

  • Equal<CamelCase<'a-b-c-'>, 'aBC-'>

如果中划线后方没有字母了,则不会对其进行操作

代码实现

在进行功能实现之前,我们先来认识一个TS函数:Capitalize(将首字母转大写)

/**
 * Convert first character of string literal type to uppercase
 */
type Capitalize<S extends string> = intrinsic;
  • 原代码
type CamelCase<S> = any;
  • 传入的 S 是一个字符串
type CamelCase<S extends string> = any;
  • 将其根据中划线(-)进行拆分,如果能拆出来则进行下一步操作,拆不出来则直接返回 S
type CamelCase<S extends string> = S extends `${infer F}-${infer R}` 
	? ...
	: S
  • 判断拆分处理的右侧字符串,首字母是否是大写
    • 如果是大写,则不删除中划线直接对右侧字符串递归调用CamelCase
    • 如果不是大写,则删除中划线,并将右侧首字母改为大写后,再递归 CamelCase
type CamelCase<S extends string> = S extends `${infer F}-${infer R}` 
	? R extends Capitalize<R>
		? `${F}-${CamelCase<R>}`
		: `${F}${CamelCase<Capitalize<R>>}`
	: S

题目二十八:kebabcase

// template.ts
type KebabCase<S> = any
// test-cases.ts
import type { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<KebabCase<'FooBarBaz'>, 'foo-bar-baz'>>,
  Expect<Equal<KebabCase<'fooBarBaz'>, 'foo-bar-baz'>>,
  Expect<Equal<KebabCase<'foo-bar'>, 'foo-bar'>>,
  Expect<Equal<KebabCase<'foo_bar'>, 'foo_bar'>>,
  Expect<Equal<KebabCase<'Foo-Bar'>, 'foo--bar'>>,
  Expect<Equal<KebabCase<'ABC'>, 'a-b-c'>>,
  Expect<Equal<KebabCase<'-'>, '-'>>,
  Expect<Equal<KebabCase<''>, ''>>,
  Expect<Equal<KebabCase<'😎'>, '😎'>>,
]

测试用例

  • Equal<KebabCase<'FooBarBaz'>, 'foo-bar-baz'>

如果是首字母大写,则直接转换成小写

如果是非首字母大写,则将其转换成小写后,前方添加中划线(-

  • Equal<KebabCase<'foo_bar'>, 'foo_bar'>

只对中划线做处理

首字母小写的TS函数

/**
 * Convert first character of string literal type to lowercase
 */
type Uncapitalize<S extends string> = intrinsic;

代码实现

  • 原代码
type KebabCase<S> = any
  • 入参 S 是一个字符串
type KebabCase<S extends string> = any
  • S 拆分为左右两个字符串(左边可以当作是首字母,右边是剩余部分)
    • 如果拆分出来了,则进行下一步
    • 否则直接返回 S
type KebabCase<S extends string> = S extends `${infer L}${infer R}`
	? ...
	: S
  • 首字母(L)不论如何,都要转成小写。在此基础上,判断 R 是否是首字母小写的
type KebabCase<S extends string> = S extends `${infer L}${infer R}`
	? R extends Uncapitalize<R>
		? `${Uncapitalize<L>}...`
		: `${Uncapitalize<L>}...`
	: S
  • 如果 R 是首字母小写的,则不做任何处理,接着对其递归 KebabCase
  • 否则添加中划线,再进行递归(不需要特意加上Uncapitalize函数,因为KebabCase 本身就含有首字母小写的功能了)
type KebabCase<S extends string> = S extends `${infer L}${infer R}`
	? R extends Uncapitalize<R>
		? `${Uncapitalize<L>}${KebabCase<R>}`
		: `${Uncapitalize<L>}-${KebabCase<R>}`
	: S