ts类型挑战【十四】

181 阅读3分钟

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

题目十八:type-lookup

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

interface Cat {
  type: 'cat'
  breeds: 'Abyssinian' | 'Shorthair' | 'Curl' | 'Bengal'
}

interface Dog {
  type: 'dog'
  breeds: 'Hound' | 'Brittany' | 'Bulldog' | 'Boxer'
  color: 'brown' | 'white' | 'black'
}

type Animal = Cat | Dog

type cases = [
  Expect<Equal<LookUp<Animal, 'dog'>, Dog>>,
  Expect<Equal<LookUp<Animal, 'cat'>, Cat>>,
]

有时,您可能希望根据其属性在并集中查找类型。

在此挑战中,我们想通过在联合Cat | Dog中搜索公共type字段来获取相应的类型。换句话说,在以下示例中,我们期望LookUp<Dog | Cat, 'dog'>获得DogLookUp<Dog | Cat, 'cat'>获得Cat

代码实现

  • 原代码
type LookUp<T, U> = any
  • 传入的参数 T、U

传入的 T 是一个联合类型,并且每一项(object)中都有 type 字段;

传入的 U 是一个字符串类型,对应 type 字段,用作筛选

type LookUp<T extends { type: string }, U extends string> = any
  • 判断 T 是否 extends 相应的 U,如果是的话,就直接返回 T,否则返回 never

这里其实隐含了联合类型的遍历

type LookUp<T extends { type: string }, U extends string> = T extends { type: U } ? T : never

题目十九:trimleft

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

type cases = [
  Expect<Equal<TrimLeft<'str'>, 'str'>>,
  Expect<Equal<TrimLeft<' str'>, 'str'>>,
  Expect<Equal<TrimLeft<'     str'>, 'str'>>,
  Expect<Equal<TrimLeft<'     str     '>, 'str     '>>,
  Expect<Equal<TrimLeft<'   \n\t foo bar '>, 'foo bar '>>,
  Expect<Equal<TrimLeft<''>, ''>>,
  Expect<Equal<TrimLeft<' \n\t'>, ''>>,
]

实现 TrimLeft<T> ,它接收确定的字符串类型并返回一个新的字符串,其中新返回的字符串删除了原字符串开头的空白字符串。

例如

type trimed = TrimLeft<'  Hello World  '> // 应推导出 'Hello World  '

代码实现

整体思想和数组的扩展运算符相似

  • 原代码
type TrimLeft<T extends string> = any
  • 空白字符串

空白字符串有三种:" "(空格符)、"\n"(换行符)、"\t"(制表符)

  • 使用字符串的扩展运算符
type TrimLeft<T extends string> = T extends `${" " | "\n" | "\t"}${infer Rest}`
	? TrimLeft<Rest>
	: T

题目二十:capitalize

// template.ts
type MyCapitalize<T extends string> = any
// test-cases.ts
import { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<MyCapitalize<'foobar'>, 'Foobar'>>,
  Expect<Equal<MyCapitalize<'FOOBAR'>, 'FOOBAR'>>,
  Expect<Equal<MyCapitalize<'foo bar'>, 'Foo bar'>>,
  Expect<Equal<MyCapitalize<''>, ''>>,
  Expect<Equal<MyCapitalize<'a'>, 'A'>>,
  Expect<Equal<MyCapitalize<'b'>, 'B'>>,
]

实现 Capitalize<T> 它将字符串的第一个字母转换为大写,其余字母保持原样。

代码实现

  • 原代码
type MyCapitalize<T extends string> = any
  • 从字符串中分解出首字母
type MyCapitalize<T extends string> = T extends `${infer First}${infer Rest}`
  • 转换大写 Uppercase
type MyCapitalize<T extends string> = T extends `${infer First}${infer Rest}`
	? `${Uppercase<First>}${Rest}`
	: T

Uppercase

来看一下 TS 内置方法 Uppercase 的实现

type Uppercase<S extends string> = intrinsic;

可以看到,这里只是简单的给了 intrinsic 这个关键字,就如它的含义一样,intrinsicTS 内部用到的。

和其它TS提供的内置类型一样,Uppercase 这几个内置类型也是为了方便类型书写,但是专门针对字符串类型<字符串字面量、模板字符串>而提供的。

它们的共同特点是,用它们生成的类型涉及到了的转换,而不是类型的转换,而这在TS里通过已有的类型书写方式是无法表达、也不太适合去表达的。

所以 TS 只能以内置关键字 instrinsic 来通过编译期来实现

官方解释:Intrinsic String Manipulation Types

为了帮助进行字符串操作,TypeScript 包括一组可用于字符串操作的类型。

为了提高性能,这些类型内置于编译器中,而在 .d.ts文件中无法找到