“这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战”
背景
因为平常自己用TS,用的不深,只是简单的使用,然后想做做题加深理解,下面是自己做的一些题目,长期更新
自己做的题库
TS 练习题
- 实现Pick
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = MyPick<Todo, 'title' | 'completed'>
const todo: TodoPreview = {
title: 'Clean room',
completed: false,
}
答案:
type MyPick<T, K extends keyof T> = {
[P in K]: T[K]
};
- 为 numCompare 函数添加类型注解。
function numCompare(first, second) {
return first >= second ? first : second;
}
答:
function numCompare(first: number, second: number): number {
return first >= second ? first : second;
}
- 解决 inputBgChange 函数报错,使函数能正常运行
function inputBgChange(): void {
let oInput: HTMLInputElement;
if (document.querySelector('.oInput')) {
oInput = document.querySelector('#oInput');
oInput.style.background = 'red';
} else {
oInput = document.createElement('input');
oInput.id = 'oInput';
oInput.style.background = 'red';
document.body.appendChild(oInput);
}
}
答:
使用类型断言解决问题
function inputBgChange(): void {
let oInput: HTMLInputElement;
if (document.querySelector('.oInput')) {
oInput = document.querySelector('#oInput') as HTMLInputElement;
oInput.style.background = 'red';
} else {
oInput = document.createElement('input');
oInput.id = 'oInput';
oInput.style.background = 'red';
document.body.appendChild(oInput);
}
}
- 补充完整接口 User 的定义,并且为 users 提供更加准确的类型注解。
interface User {
// todo
}
// 将 unknow 替换成更准确的类型
let users: unknown = [
{
name: 'Jack Ma',
age: 17,
sex: 'male',
},
{
name: 'Tony Ma',
age: 18,
},
]
答:
interface User {
// todo
name: string,
age: number,
sex?: string
}
// 将 unknow 替换成更准确的类型
let users: Array<User> = [
{
name: 'Jack Ma',
age: 17,
sex: 'male',
},
{
name: 'Tony Ma',
age: 18,
},
]
- 实现一个 If 工具泛型,接受一个条件 C,若 C 为 true 返回类型 T 类型,否则返回 F 类型。
type If<C, T, F> = any; // todo
type T1 = If<true, boolean, number>;
// T1 为 boolean 类型
type T2 = If<false, boolean, number>;
// T2 为 number 类型
答:
type If<C extends boolean, T, F> = C extends true ? T : F; // todo
type T1 = If<true, boolean, number>;
// T1 为 boolean 类型
type T2 = If<false, boolean, number>;
// T2 为 number 类型
- 实现一个 post 方法,当我们请求地址 url 为 /user/add 时,请求参数 params 中必传字段为名字 name 和年龄 age,性别 sex 为选填字段,其中除了 age 字段类型为 number,其余字段类型都为 string。
import axios from 'axios';
function post(url: any, params: any) {
return axios.post(url, params)
}
post('/user/del', {name: 'jack Ma', age: 17});
// 报错, url 传参错误
post('/user/add', {name: 'jack Ma'});
// 报错, 缺少请求参数 age 字段
post('/user/add', {name: 'jack Ma', age: 17});
// 请求成功
post('/user/add', {name: 'jack Ma', age: 17, sex: 'male'})
// 请求成功
答案:
interface API {
'/user/add': {
name: string,
age: number,
sex?: string
}
}
function post<T extends keyof API>(url: T, params: API[T]) {
return axios.post(url, params);
}
post('/user/del', {name: 'jack Ma', age: 17});
// 报错, url 传参错误
post('/user/add', {name: 'jack Ma'});
// 报错, 缺少请求参数 age 字段
post('/user/add', {name: 'jack Ma', age: 17});
// 请求成功
post('/user/add', {name: 'jack Ma', age: 17, sex: 'male'})
// 请求成功
- 实现一个 Includes<T, K> 工具泛型,T 为一个数组类型,判断 K 是否存在于 T 中,若存在返回 true,否则返回 false
type T1 = Includes<['name','age','sex'], 'name'>
// T1 的期望为 true
type T2 = Includes<['name','age','sex'], 'name'>
// T2 的期望为 false
答案:
type Includes<T extends any[], K> = K extends T[number] ? true : false;
- 实现一个 MyReadOnly<T, K> ,K 应为 T 的属性集,若指定 K ,则将 T 中对应的属性修改成只读属性,若不指定 K ,则将所有属性变为只读属性
interface Person {
name: string,
age: number,
}
type MyReadOnly<T, K> = any;
const jack: MyReadOnly<Person, 'name'> = {
name: 'jack',
age: 17,
}
jack.name = 'jack';
// error
jcak.age = 18;
// success
答案:
type MyReadOnly<T, K extends keyof T = keyof T> = {
readonly [P in K]: T[P]
} & T;
- 实现一个 AppendArgX<Fn, X> 工具泛型,对于给定的函数类型 Fn, 和任意的类型 X,在 Fn 函数的传参末尾追加类型为 X 的参数
type Fn = (a: number, b: string) => number
type NewFn = AppendArgX<Fn, boolean>
// NewFn 期望是 (a: number, b: string, x: boolean) => number
答案:
type AppendArgX<Fn, X> = Fn extends (...args: infer P) => infer R ? (...args: [...P, X])=>R : never;
- 实现一个 GetRequired 工具泛型,将 T 中的所有必需属性提取出来
type RequiredKeys<T> = keyof T extends infer K
? K extends keyof T
? T[K] extends Required<T>[K]
? K
: never
: never
: never;
// Required<T> 是 TypeScript 内置的工具泛型,可以将 T 所有属性设置为必需属性
type GetRequired<T> = {
[key in RequiredKeys<T>]: T[key]
};
- 实现 Readonly
interface Todo {
title: string
description: string
}
const todo: MyReadonly<Todo> = {
title: "Hey",
description: "foobar"
}
todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property
答案:
type MyReadonly<T> = {
readonly [P in keyof T]: T[P]
};
- 元组转换为对象
const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const
const result: TupleToObject<typeof tuple> // expected { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}
答案:
type TupleToObject<T extends readonly any[]> = {
[Property in T[number]]: Property
};
- 实现Record
答案:
type MyRecord<T extends typeof any, U> = {
[K in T]: U
}
- 实现 Exclude
把某个类型中,属于另一个的类型移除掉
答案:
// 如果T能够赋值给U类型的话,那么就会返回never类型,否则返回T类型
// 实现的效果就是把T类型中的U类型给排除掉
type Exclude<T, U> = T extends U ? never: T;
- 去除所有never成员
答案:
使用{}[keyof T] 把对象类型变成联合类型
type OmitNever<T> = Pick<T, {
[K in keyof T]: T[K] extends never ? never : K
}[keyof T]>
type T123 = {
a: string,
b: never,
c: string,
}
type L = OmitNever<T123>;
- 实现一个通用First,它接受一个数组T并返回它的第一个元素的类型。
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
type head1 = First<arr1> // expected to be 'a'
type head2 = First<arr2> // expected to be 3
答案:
type First<T extends any[]> = T[0] extends T[number] ? T[0] : never;
- 对于给定的元组,您需要创建一个通用的Length,选择元组的长度
type tesla = ['tesla', 'model 3', 'model X', 'model Y']
type spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT']
type teslaLength = Length<tesla> // expected 4
type spaceXLength = Length<spaceX> // expected 5
答案:
type Length<T extends any[]> = T['length'];
- 元组转换为联合类型
type TTuple = [string, number];
type Union = ElementOf<TTuple>; // Union 类型为 string | number
答案:
type ElementOf<T> = T extends (infer R)[] ? R : never;
- 实现一个工具 If,它接受条件C、true返回类型T和false返回类型F。C可以是true或false,而T和F可以是任何类型。
type A = If<true, 'a', 'b'> // expected to be 'a'
type B = If<false, 'a', 'b'> // expected to be 'b'
答案:
type If<C extends boolean, T, F> = C extends true ? T: F;
- 在类型系统中实现JavaScript Array.concat函数。类型接受两个参数。输出应该是一个新的数组,其中包括ltr顺序的输入
type Result = Concat<[1], [2]> // expected to be [1, 2]
答案:
type Concat<T extends any[], U extends any[]> = [...T, ...U];
type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // expected to be `false`
答案:
type Includes<T extends any[], U> = {
[K in T[number]]: true
}[U] extends true ? true : false;
- 关于infer的经典题目,实现ReturnType
const fn = (v: boolean) => {
if (v)
return 1
else
return 2
}
type a = MyReturnType<typeof fn> // 应推导出 "1 | 2"
type MyReturnType<T> = T extends (...args: any) => infer R ? R : never;
- 不使用 Omit 实现 TypeScript 的 Omit<T, K> 范型。Omit 会创建一个省略 K 中字段的 T 对象。
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = MyOmit<Todo, 'description' | 'title'>;
const todo1233: TodoPreview = {
completed: false,
}
答案:
type MyOmit<T, K> = {
[P in Exclude<keyof T, K>]: T[P]
};
- 写一个deepReadonly
type deepReadonly<T> = {
readonly [K in keyof T]: T[K] extends any[] | number | string | boolean | Function ? T[K] : deepReadonly<T[K]>
}
- 元组转合集
type TupleToUnion<T extends any[]> = T[number]
未完待续。。。