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

317 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情

1. 引言

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

2. 题型

  1. PickByType:从T中选择一组类型可赋值给U的属性。
type OnlyBoolean = PickByType<{
  name: string
  count: number
  isReadonly: boolean
  isEnable: boolean
}, boolean> // { isReadonly: boolean; isEnable: boolean; }

思路:    使用映射类型获取T 变量中的key和对应键值,在通过as类型断言为当键值为目标U对应类型值时候,返回对应的键和键值,否则则过滤对应键和键值。 解答:

type PickByType<T, U> = {
  [P in keyof T as T[P] extends U ? P : never]: T[P]
}
interface Model {
  name: string
  count: number
  isReadonly: boolean
  isEnable: boolean
}

type Demo = PickByType<Model, boolean> // type Demo = {isReadonly: boolean;isEnable: boolean;}
type Demo2 = PickByType<Model, string> // type Demo2 = {name: string;}
type Demo3 = PickByType<Model, number> // type Demo3 = {count: number;}

  1. StartsWith:实现StartsWith<T, U>,接收两个string类型参数,然后判断T是否以U开头,根据结果返回true或false
type a = StartsWith<'abc', 'ac'> // expected to be false
type b = StartsWith<'abc', 'ab'> // expected to be true
type c = StartsWith<'abc', 'abcd'> // expected to be false

思路:    使用类型判断灵活判断是否存在目标字符串。 解答:

type StartsWith<T extends string, U extends string> = T extends `${U}${string}` ? true : false
type Demo = StartsWith<'abc', 'ac'> // type Demo = false
type Demo2 = StartsWith<'abc', 'ab'> // type Demo2 = true
type Demo3 = StartsWith<'abc', 'abcd'> // type Demo3 = false
type Demo4 = StartsWith<'abc', ''> // type Demo4 = true
type Demo5 = StartsWith<'abc', ' '> // type Demo5 = false

  1. EndsWith:实现EndsWith<T, U>,接收两个string类型参数,然后判断T是否以U结尾,根据结果返回true或false
type a = EndsWith<'abc', 'bc'> // expected to be false
type b = EndsWith<'abc', 'abc'> // expected to be true
type c = EndsWith<'abc', 'd'> // expected to be false

思路:    方法同上。 解答:

type EndsWith<T extends string, U extends string> = T extends `${string}${U}` ? true : false
type Demo = EndsWith<'abc', 'bc'> // type Demo = true
type Demo2 = EndsWith<'abc', 'abc'> // type Demo = true
type Demo3 = EndsWith<'abc', 'd'> // type Demo = false

  1. PartialByKeys:实现一个通用的PartialByKeys<T, K>,它接收两个类型参数T和K。 K指定应设置为可选的T的属性集。当没有提供K时,它就和普通的Partial一样使所有属性都是可选的。
interface User {
  name: string
  age: number
  address: string
}

type UserPartialName = PartialByKeys<User, 'name'> // { name?:string; age:number; address:string }

思路:    首先确定解题基本思路,这题需要组合类型分别为①:Omit 过滤T中除了U的其他类型;②:Partial 把T中所有的类型装换为可选类型;再通过类型合并merge的方式组合合并两个类型获得除了设置目标类型除了U为可选类型,其他类型都为必填类型; 在这题中有两个小点要注意:①为什么要使用merge方法合并类型而不是直接使用“&”符号合并两个类型:是因为当使用& 返回的是对象的交叉类型,并不是合并类型;②PropertyKey = keyof 的使用可以过滤当U类型为联合类型并且其中有个类型为unknown 的时候。 解答:

type Merge<T> = {
  [P in keyof T]: T[P]
}
type PartialByKeys<T, U extends PropertyKey = keyof T> = Merge<Partial<T> & Omit<T, U>>
type Demo = PartialByKeys<User,'name'> // type Demo = {name?: string | undefined;age: number;address: string;}
type Demo2 = PartialByKeys<User,'name' | 'unknown'> // type Demo = {name?: string | undefined;age: number;address: string;}
type Demo3 = PartialByKeys<User,'name' | 'age'> // type Demo = {name?: string | undefined;age?: number;address: string;}
type Demo4 = PartialByKeys<User> // type Demo4 = {name?: string | undefined;ge?: number | undefined;address?: string | undefined;}