一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第24天,点击查看活动详情。
题目三十七:pickbytype
// template.ts
type PickByType<T, U> = any
// test-case.ts
import type { Equal, Expect } from '@type-challenges/utils'
interface Model {
name: string
count: number
isReadonly: boolean
isEnable: boolean
}
type cases = [
Expect<Equal<PickByType<Model, boolean>, { isReadonly: boolean; isEnable: boolean }>>,
Expect<Equal<PickByType<Model, string>, { name: string }>>,
Expect<Equal<PickByType<Model, number>, { count: number }>>,
]
从“T”中,选择一组类型可分配给“U”的属性。
测试用例
Equal<PickByType<Model, boolean>, { isReadonly: boolean; isEnable: boolean }>
从 Model 中拿出值为 boolean 的内容,期望结果为 :{ isReadonly: boolean; isEnable: boolean }
Equal<PickByType<Model, string>, { name: string }>
从 Model 中拿出值为 string 的内容,期望结果为 :{ name: string }
Equal<PickByType<Model, number>, { count: number }>
从 Model 中拿出值为 number 的内容,期望结果为 :{ count: number }
代码实现
- 原代码
type PickByType<T, U> = any
- 因为我们需要从
T中获取特定的内容,那么我们先对T进行遍历
type PickByType<T, U> = {
[P in keyof T]: T[P]
}
- 遍历过程中,需要去除一些不必要的项,只留下存在于
U中相对应的项
做法:将 keyof T 进行转换 (as)
看看值(T[P])是否在 U 中,如果存在则正常返回 P,不存在则返回 never(返回 never 的话,当前循环项就不会出现在结果中了)
type PickByType<T, U> = {
[P in keyof T as T[P] extends U ? P : never]: T[P]
}
题目三十八:startswith
// template.ts
type StartsWith<T extends string, U extends string> = any
// test-cases.ts
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [
Expect<Equal<StartsWith<'abc', 'ac'>, false>>,
Expect<Equal<StartsWith<'abc', 'ab'>, true>>,
Expect<Equal<StartsWith<'abc', 'a'>, true>>,
Expect<Equal<StartsWith<'abc', 'abcd'>, false>>,
]
实现 'StartsWith<T,U>',它接受两种精确的字符串类型,并返回'T'是否以'U'开头
代码实现
- 原代码
type StartsWith<T extends string, U extends string> = any
- 直接对
T进行拆分,但是拆分内容中包含U
type StartsWith<T extends string, U extends string> = T extends `${U}${infer _}` ? true : false
- 也可以写成
type StartsWith<T extends string, U extends string> = T extends `${U}${string}` ? true : false
题目三十九:partialbykeys
// template.ts
type PartialByKeys<T, K> = any
// test-cases.ts
import type { Equal, Expect } from '@type-challenges/utils'
interface User {
name: string
age: number
address: string
}
interface UserPartialName {
name?: string
age: number
address: string
}
interface UserPartialNameAndAge {
name?: string
age?: number
address: string
}
type cases = [
Expect<Equal<PartialByKeys<User, 'name'>, UserPartialName>>,
Expect<Equal<PartialByKeys<User, 'name' | 'unknown'>, UserPartialName>>,
Expect<Equal<PartialByKeys<User, 'name' | 'age'>, UserPartialNameAndAge>>,
Expect<Equal<PartialByKeys<User>, Partial<User>>>,
]
实现一个通用的 PartialByKeys<T,K>,它接受两种类型的参数 T 和 K。
K指定应设置为可选的T属性集。当没有提供 K 时,它应该使所有属性都是可选的,就像正常的 Partial<T> 。
测试用例
interface User {
name: string
age: number
address: string
}
PartialByKeys<User, 'name'>
将 User 中的 name 属性变为可选,期望结果:{ name?: string, age: number, address: string }
PartialByKeys<User, 'name' | 'unknown'>
联合类型中的 unknown 不会影响结果
PartialByKeys<User>
如果不传入第二个参数,则全部变为可选
代码实现
- 原代码
type PartialByKeys<T, K> = any
- 使用
Omit拿到K之外的内容。使用K & keyof T去除T之外的内容,例如:unknown
type PartialByKeys<T, K> = Omit<T, K & keyof T>
- 合并剩余的
K中的内容,并加上可选符号?
type PartialByKeys<T, K> = Omit<T, K & keyof T> & {
[P in K & keyof T]?: T[P]
}
- 此时还是报错状态,还需要对齐遍历(Copy)一份,因为
Omit合并的缘故
type Copy<T> = {
[P in keyof T]: T[P]
}
type PartialByKeys<T, K = keyof T> = Copy<Omit<T, K & keyof T> & {
[P in K & keyof T]?: T[P]
}>