【Typescript 系列】类型体操之中等篇题型(第八节)解读

94 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情

1. 引言

接着上一节中,接下来我们继续Ts中等篇的题型练习 https://github.com/type-challenges/type-challenges/blob/main/README.zh-CN.md 提供的TypeScript 类型体操姿势合集题型,目的是为了让大家更好的了解TS的类型系统,编写自己的类型工具,或者单纯的享受挑战的乐趣!

2. 题型

  1. String to Union:实现一个将接收到的String参数转换为一个字母Union的类型。
type Test = '123';
type Result = StringToUnion<Test>; // expected to be "1" | "2" | "3"

思路:    首先解析题目,传入一个string类型变量,返回形参成员属性组合而成的联合类型。通过模板字面量类型和infer关键字拆分形参参数第一个成员值,之后配合递归操作获取后面成员属性,在通过联合类型操作合并类型,返回所有成员的联合类型。 解答:

type StringToUnion<T extends string> = T extends `${infer M}${infer N}` ? `${M | StringToUnion<N>}` : never
type Demo = StringToUnion<''> // type Demo = never
type Demo2 = StringToUnion<'t'> // type Demo2 = "t"
type Demo3 = StringToUnion<'hello'> // type Demo3 = "h" | "e" | "l" | "o"
type Demo4 = StringToUnion<'coronavirus'> // type Demo4 = "o" | "c" | "r" | "n" | "a" | "v" | "i" | "u" | "s"

  1. Merge:将两个类型合并成一个类型,第二个类型的键会覆盖第一个类型的键。
type foo = {
  name: string;
  age: string;
}

type coo = {
  age: number;
  sex: string
}

type Result = Merge<foo,coo>; // expected to be {name: string, age: number, sex: string}

思路:    首先合并类型第一要想到的就是映射类型操作符,其次要注意该题目第二个类型的键会覆盖第一个类型的键 的要求,说明在使用映射类型操作的时候,形参二的回抛参数优先与形参一,通过联合类型操作及extends条件判读即可类型合并。 解答:

type Merge<F, S> = {
  [P in keyof F | keyof S]: P extends keyof S ? S[P] : P extends keyof F ? F[P] : never
}
type Foo = {
  a: number
  b: string
}
type Bar = {
  b: number
  c: boolean
}
type Demo = Merge<Foo, Bar> // type Demo = {a: number;b: number;c: boolean;}
  1. KebabCase
FooBarBaz -> foo-bar-bazsymbol

思路:    首先KebabCase 接受两个形参,分别是被检测字段及控制是否在字段内部新增'-'形参。观察被检字段,第一步:通过 ${infer F}${infer Rest} 过滤空字符串;第二步:通过**Uppercase extends Lowercase过滤异性符号;第三步抽离当前字段判断是否为大写后,是:改为小写,否:返回原字符串;后续执行递归操作时第二参数形参传入true值,在第二步中执行false语句的前置判断中`${Uppercase extends F ? IsR extends true ? '-' : '' : ''}**给驼峰参数改为小写的同时,加上‘-’字符串。

解答:

type KebabCase<S extends string, IsR = false> =
  S extends `${infer F}${infer Rest}`
    ? Uppercase<F> extends Lowercase<F> ?
      `${F}${KebabCase<Rest, true>}`
      : `${Uppercase<F> extends F ? IsR extends true ? '-' : '' : ''}${Uppercase<F> extends F ? Lowercase<F> : F}${KebabCase<Rest, true>}`
    : S;
type Demo = KebabCase<'FooBarBaz'> // type Demo = "foo-bar-baz"
type Demo2 = KebabCase<'fooBarBaz'> // type Demo3 = "foo-bar"
type Demo3 = KebabCase<'foo-bar'> // type Demo3 = "foo-bar"
type Demo4 = KebabCase<'foo_bar'> // type Demo4 = "foo_bar"
type Demo5 = KebabCase<'Foo-Bar'> // type Demo5 = "foo--bar"
type Demo6 = KebabCase<'-'> // type Demo6 = "-"
type Demo7 = KebabCase<''> // type Demo7 = ""sz900cxxxxxx