一起养成写作习惯!这是我参与「掘金日新计划 · 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'>获得Dog,LookUp<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 这个关键字,就如它的含义一样,intrinsic 是 TS 内部用到的。
和其它TS提供的内置类型一样,Uppercase 这几个内置类型也是为了方便类型书写,但是专门针对字符串类型<字符串字面量、模板字符串>而提供的。
它们的共同特点是,用它们生成的类型涉及到了值的转换,而不是类型的转换,而这在TS里通过已有的类型书写方式是无法表达、也不太适合去表达的。
所以 TS 只能以内置关键字 instrinsic 来通过编译期来实现
官方解释:Intrinsic String Manipulation Types
为了帮助进行字符串操作,
TypeScript包括一组可用于字符串操作的类型。为了提高性能,这些类型内置于编译器中,而在
.d.ts文件中无法找到