又更新了 20 道 TS 练习题,你能答对几道?

中秋假期已经结束了,由于疫情原因阿宝哥在家里待了三天,期间精选了 20 道新的 TS 练习题,目前已经有 30 道题了。

很多小伙伴已提交了他们心目中的答案,撸过 TS 的小伙伴赶紧来练练手👇。

https://github.com/semlinker/awesome-typescript/issues

前十题的参考答案 那 10 道 TS 练习题的参考答案来咯!,详细解析的 PPT 还在制作中,目前还在学习一款功能强大的 Markdown 创建 Web PPT 的工具,请小伙伴耐心等待。

Tser 一起练起来,答题时可以考虑直接使用在线 TS Playground,选用最新的编译器版本:v4.4.2

第一题

type User = {
  id: number;
  kind: string;
};

function makeCustomer<T extends User>(u: T): T {
  // Error(TS 编译器版本:v4.4.2)
  // Type '{ id: number; kind: string; }' is not assignable to type 'T'.
  // '{ id: number; kind: string; }' is assignable to the constraint of type 'T', 
  // but 'T' could be instantiated with a different subtype of constraint 'User'.
  return {
    id: u.id,
    kind: 'customer'
  }
}

以上代码为什么会提示错误,应该如何解决上述问题?

第二题

本道题我们希望参数 a 和 b 的类型都是一致的,即 a 和 b 同时为 number 或 string 类型。当它们的类型不一致的值,TS 类型检查器能自动提示对应的错误信息。

function f(a: string | number, b: string | number) {
  if (typeof a === 'string') {
    return a + ':' + b; // no error but b can be number!
  } else {
    return a + b; // error as b can be number | string
  }
}

f(23); // Ok
f(1'a'); // Error
f('a'2); // Error
f('a''b'// Ok

第三题

在 掌握 TS 这些工具类型,让你开发事半功倍 这篇文章中,阿宝哥介绍了 TS 内置的工具类型:Partial<T>,它的作用是将某个类型里的属性全部变为可选项 ?

interface Todo {
  title: string;
  description: string;
}

function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
  return { ...todo, ...fieldsToUpdate };
}

// lib.es5.d.ts
type Partial<T> = {
  [P in keyof T]?: T[P];
};

那么如何定义一个 SetOptional 工具类型,支持把给定的 keys 对应的属性变成可选的?对应的使用示例如下所示:

type Foo = {
 anumber;
 b?: string;
 cboolean;
}

// 测试用例
type SomeOptional = SetOptional<Foo'a' | 'b'>;

// type SomeOptional = {
//  a?: number; // 该属性已变成可选的
//  b?: string; // 保持不变
//  c: boolean; 
// }

在实现 SetOptional 工具类型之后,如果你感兴趣,可以继续实现 SetRequired 工具类型,利用它可以把指定的 keys 对应的属性变成必填的。对应的使用示例如下所示:

type Foo = {
 a?: number;
 bstring;
 c?: boolean;
}

// 测试用例
type SomeRequired = SetRequired<Foo'b' | 'c'>;
// type SomeRequired = {
//  a?: number;
//  b: string; // 保持不变
//  c: boolean; // 该属性已变成必填
// }

第四题

Pick<T, K extends keyof T> 的作用是将某个类型中的子属性挑出来,变成包含这个类型部分属性的子类型。

interface Todo {
  titlestring;
  descriptionstring;
  completedboolean;
}

type TodoPreview = Pick<Todo"title" | "completed">;

const todoTodoPreview = {
  title"Clean room",
  completedfalse
};

那么如何定义一个 ConditionalPick 工具类型,支持根据指定的 Condition 条件来生成新的类型,对应的使用示例如下:

interface Example {
 astring;
 bstring | number;
 c() => void;
 d: {};
}

// 测试用例:
type StringKeysOnly = ConditionalPick<Examplestring>;
//=> {a: string}

第五题

定义一个工具类型 AppendArgument,为已有的函数类型增加指定类型的参数,新增的参数名是 x,将作为新函数类型的第一个参数。具体的使用示例如下所示:

type Fn = (a: number, b: string) => number
type AppendArgument<F, A> = // 你的实现代码

type FinalFn = AppendArgument<Fnboolean// (x: boolean, a: number, b: string) => number

第六题

定义一个 NativeFlat 工具类型,支持把数组类型拍平(扁平化)。具体的使用示例如下所示:

type NaiveFlat<T extends any[]> = // 你的实现代码

// 测试用例:
type NaiveResult = NaiveFlat<[['a'], ['b', 'c'], ['d']]>
// NaiveResult的结果: "a" | "b" | "c" | "d"

在完成 NaiveFlat 工具类型之后,在继续实现 DeepFlat 工具类型,以支持多维数组类型:

type DeepFlat<T extends any[]> = unknown // 你的实现代码

// 测试用例
type Deep = [['a'], ['b''c'], [['d']], [[[['e']]]]];
type DeepTestResult = DeepFlat<Deep>  
// DeepTestResult: "a" | "b" | "c" | "d" | "e"

第七题

使用类型别名定义一个 EmptyObject 类型,使得该类型只允许空对象赋值:

type EmptyObject = {} 

// 测试用例
const shouldPass: EmptyObject = {}; // 可以正常赋值
const shouldFail: EmptyObject = { // 将出现编译错误
  prop: "TS"
}

在通过 EmptyObject 类型的测试用例检测后,我们来更改以下 takeSomeTypeOnly 函数的类型定义,让它的参数只允许严格SomeType类型的值。具体的使用示例如下所示:

type SomeType =  {
  prop: string
}

// 更改以下函数的类型定义,让它的参数只允许严格SomeType类型的值
function takeSomeTypeOnly(x: SomeTypereturn x }

// 测试用例:
const = { prop: 'a' };
takeSomeTypeOnly(x) // 可以正常调用

const = { prop: 'a', addditionalProp: 'x' };
takeSomeTypeOnly(y) // 将出现编译错误

第八题

定义 NonEmptyArray 工具类型,用于确保数据非空数组。

type NonEmptyArray<T> = // 你的实现代码

const a: NonEmptyArray<string> = [] // 将出现编译错误
const b: NonEmptyArray<string> = ['Hello TS'// 非空数据,正常使用

提示:该题目有多种解法,感兴趣小伙伴可以自行尝试一下。

第九题

定义一个 JoinStrArray 工具类型,用于根据指定的 Separator 分隔符,对字符串数组类型进行拼接。具体的使用示例如下所示:

type JoinStrArray<Arr extends string[], Separator extends stringResult extends string = ""> = // 你的实现代码

// 测试用例
type Names = ["Sem""Lolo""Kaquko"]
type NamesComma = JoinStrArray<Names","// "Sem,Lolo,Kaquko"
type NamesSpace = JoinStrArray<Names" "// "Sem Lolo Kaquko"
type NamesStars = JoinStrArray<Names"⭐️"// "Sem⭐️Lolo⭐️Kaquko"

第十题

实现一个 Trim 工具类型,用于对字符串字面量类型进行去空格处理。具体的使用示例如下所示:

type Trim<V extends string> = // 你的实现代码

// 测试用例
Trim<' semlinker '>
//=> 'semlinker'

提示:可以考虑先定义 TrimLeft 和 TrimRight 工具类型,再组合成 Trim 工具类型。

第十一题

实现一个 IsEqual 工具类型,用于比较两个类型是否相等。具体的使用示例如下所示:

type IsEqual<A, B> = // 你的实现代码

// 测试用例
type E0 = IsEqual<12>// false
type E1 = IsEqual<{ a: 1 }, { a: 1 }> // true
type E2 = IsEqual<[1], []>// false

第十二题

实现一个 Head 工具类型,用于获取数组类型的第一个类型。具体的使用示例如下所示:

type Head<T extends Array<any>> = // 你的实现代码

// 测试用例
type H0 = Head<[]> // never
type H1 = Head<[1]> // 1
type H2 = Head<[32]> // 3

提示:该题目有多种解法,感兴趣小伙伴可以自行尝试一下。

第十三题

实现一个 Tail 工具类型,用于获取数组类型除了第一个类型外,剩余的类型。具体的使用示例如下所示:

type Tail<T extends Array<any>> =  // 你的实现代码

// 测试用例
type T0 = Tail<[]> // []
type T1 = Tail<[12]> // [2]
type T2 = Tail<[12345]> // [2, 3, 4, 5]

提示:该题目有多种解法,感兴趣小伙伴可以自行尝试一下。

第十四题

实现一个 Unshift 工具类型,用于把指定类型 E 作为第一个元素添加到 T 数组类型中。具体的使用示例如下所示:

type Unshift<T extends any[], E> =  // 你的实现代码

// 测试用例
type Arr0 = Unshift<[], 1>; // [1]
type Arr1 = Unshift<[123], 0>; // [0, 1, 2, 3]

提示:该题目有多种解法,感兴趣小伙伴可以自行尝试一下。

第十五题

实现一个 Shift 工具类型,用于移除 T 数组类型中的第一个类型。具体的使用示例如下所示:

type Shift<T extends any[]> = // 你的实现代码

// 测试用例
type S0 = Shift<[123]> // [2, 3]
type S1 = Shift<[string,number,boolean]> // [number,boolean]

第十六题

实现一个 Push 工具类型,用于把指定类型 E 作为最后一个元素添加到 T 数组类型中。具体的使用示例如下所示:

type Push<T extends any[], V> = // 你的实现代码

// 测试用例
type Arr0 = Push<[], 1// [1]
type Arr1 = Push<[123], 4// [1, 2, 3, 4]

第十七题

实现一个 Includes 工具类型,用于判断指定的类型 E 是否包含在 T 数组类型中。具体的使用示例如下所示:

type Includes<T extends Array<any>, E> = // 你的实现代码

type I0 = Includes<[], 1> // false
type I1 = Includes<[2231], 2> // true
type I2 = Includes<[2331], 1> // true

提示:该题目有多种解法,感兴趣小伙伴可以自行尝试一下。

第十八题

实现一个 UnionToIntersection 工具类型,用于把联合类型转换为交叉类型。具体的使用示例如下所示:

type UnionToIntersection<U> = // 你的实现代码

// 测试用例
type U0 = UnionToIntersection<string | number// never
type U1 = UnionToIntersection<{ namestring } | { agenumber }> // { name: string; } & { age: number; }

第十九题

实现一个 OptionalKeys 工具类型,用来获取对象类型中声明的可选属性。具体的使用示例如下所示:

type Person = {
  id: string;
  name: string;
  age: number;
  from?: string;
  speak?: string;
};

type OptionalKeys<T> = // 你的实现代码
type PersonOptionalKeys = OptionalKeys<Person> // "from" | "speak"

提示:该题目有多种解法,感兴趣小伙伴可以自行尝试一下。

第二十题

实现一个 Curry 工具类型,用来实现函数类型的柯里化处理。具体的使用示例如下所示:

type Curry<
  F extends (...argsany[]) => any,
  P extends any[] = Parameters<F>, 
  R = ReturnType<F> 
> = // 你的实现代码

type F0 = Curry<() => Date>; // () => Date
type F1 = Curry<(a: number) => Date>; // (arg: number) => Date
type F2 = Curry<(a: number, b: string) => Date>; //  (arg_0: number) => (b: string) => Date

第二十一题

实现一个 Merge 工具类型,用于把两个类型合并成一个新的类型。第二种类型(SecondType)的 Keys 将会覆盖第一种类型(FirstType)的 Keys。具体的使用示例如下所示:

type Foo = {
 anumber;
 bstring;
};

type Bar = {
 bnumber;
};

type Merge<FirstTypeSecondType> = // 你的实现代码

const abMerge<FooBar> = { a1b2 };

第二十二题

实现一个 RequireAtLeastOne 工具类型,它将创建至少含有一个给定 Keys 的类型,其余的 Keys 保持原样。具体的使用示例如下所示:

type Responder = {
 text?: () => string;
 json?: () => string;
 secure?: boolean;
};

type RequireAtLeastOne<
 ObjectType,
 KeysType extends keyof ObjectType = keyof ObjectType,
> = // 你的实现代码

// 表示当前类型至少包含 'text' 或 'json' 键
const responderRequireAtLeastOne<Responder'text' | 'json'> = {
 json() => '{"message": "ok"}',
 securetrue
};

第二十三题

实现一个 RemoveIndexSignature 工具类型,用于移除已有类型中的索引签名。具体的使用示例如下所示:

interface Foo {
  [keystring]: any;
  [keynumber]: any;
  bar(): void;
}

type RemoveIndexSignature<T> = // 你的实现代码

type FooWithOnlyBar = RemoveIndexSignature<Foo>; //{ bar: () => void; }

第二十四题

实现一个 Mutable 工具类型,用于移除对象类型上所有属性或部分属性的 readonly 修饰符。具体的使用示例如下所示:

type Foo = {
  readonly anumber;
  readonly bstring;
  readonly cboolean;
};

type Mutable<T, Keys extends keyof T = keyof T> = // 你的实现代码

const mutableFooMutable<Foo'a'> = { a1b'2'ctrue };

mutableFoo.a = 3// OK
mutableFoo.b = '6'// Cannot assign to 'b' because it is a read-only property.

第二十五题

实现一个 IsUnion 工具类型,判断指定的类型是否为联合类型。具体的使用示例如下所示:

type IsUnion<T, U = T> = // 你的实现代码

type I0 = IsUnion<string|number// true
type I1 = IsUnion<string|never// false
type I2 =IsUnion<string|unknown> // false

第二十六题

实现一个 IsNever 工具类型,判断指定的类型是否为 never 类型。具体的使用示例如下所示:

type I0 = IsNever<never// true
type I1 = IsNever<never | string// false
type I2 = IsNever<null// false

第二十七题

实现一个 Reverse 工具类型,用于对元组类型中元素的位置颠倒,并返回该数组。元组的第一个元素会变成最后一个,最后一个元素变成第一个。

type Reverse<
  T extends Array<any>,
  R extends Array<any> = []
> = // 你的实现代码

type R0 = Reverse<[]> // []
type R1 = Reverse<[123]> // [3, 2, 1]

第二十八题

实现一个 Split 工具类型,根据给定的分隔符(Delimiter)对包含分隔符的字符串进行切割。可用于定义 String.prototype.split 方法的返回值类型。具体的使用示例如下所示:

type Item = 'semlinker,lolo,kakuqo';

type Split<
 S extends string, 
 Delimiter extends string,
> = // 你的实现代码

type ElementType = Split<Item','>; // ["semlinker", "lolo", "kakuqo"]

第二十九题

实现一个 ToPath 工具类型,用于把属性访问(. 或 [])路径转换为元组的形式。具体的使用示例如下所示:

type ToPath<S extends string> = // 你的实现代码

ToPath<'foo.bar.baz'//=> ['foo''bar''baz']
ToPath<'foo[0].bar.baz'//=> ['foo''0''bar''baz']

第三十题

完善 Chainable 类型的定义,使得 TS 能成功推断出 result 变量的类型。调用 option 方法之后会不断扩展当前对象的类型,使得调用 get 方法后能获取正确的类型。

declare const configChainable

type Chainable = {
  option(keystringvalueany): any
  get(): any
}

const result = config
  .option('age'7)
  .option('name''lolo')
  .option('address', { value'XiaMen' })
  .get()

type ResultType = typeof result  
// 期望 ResultType 的类型是:
// {
//   age: number
//   name: string
//   address: {
//     value: string
//   }
// }

https://mp.weixin.qq.com/s?__biz=MzI2MjcxNTQ0Nw==&mid=2247495521&idx=1&sn=dd26b5b4f2cd2c78dcbcc9fd15b27df7&scene=21#wechat_redirect

 

posted @   imxiangzi  阅读(206)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示