引言
最近团队新增了一个 hc
,面试候选人的过程中,看他简历,履历很牛逼,并且里面写的精通 typescript
。
我想那我们就问一个很基础的问题吧,你了解 typscript
的 extends
关键字吗?它都会在那几个场景下使用?
结果很失望,即使在我写出了 extends
的使用方式时,这位哥们还没想起怎么用,只能含糊其辞。
我赶紧换了一个问题,避免太尴尬:你了解 ts
工具类型 exclude
与 omit
的使用吗?及它们两个的区别?
这哥们还是不会~
不出意外的一面就挂了,说这个事的原因是,现在经济下行、市场上是狼多肉少,大家一定要珍惜面试机会,实事求是,平时多积累、多梳理,战时抓机会、少冷场啊。
好了下面说正事。
extends 关键字实现继承
如果你有看过 typescript
官方文档,起码熟悉这两个:
extends
关键字可以实现interface
类型的扩展,- 这个也是
interface
与type
类型别名实现扩展的区别之一,类型别名通过&
交叉类型来实现类型扩展
- 这个也是
extends
关键字可用于class
的继承
比如定义个 animal
接口
interface animal { name: string } interface person extends animal { level: number } const person: person = { name: 'perter', level: 1 } interface dog extends animal { leg: number } const dog: dog = { name: 'bagong', leg: 4 }
如果使用 class
class animal { move() { console.log("moving along!"); } } class person extends animal { talk(info: string) { console.log(info) } } const person = new person(); // base class method person.move(); // derived class method person.talk('hello world') class dog extends animal { woof(times: number) { for (let i = 0; i < times; i++) { console.log("woof!"); } } } const d = new dog(); // base class method d.move(); // derived class method d.woof(3);
这种方式就是在践行 extends
单词的本意 扩展
。
extends 实现类型约束
很贴切的列子就是 typescript
的工具类型 pick
,可以通过从一个 类型中选取一组属性集合来构造一个新的类型。
接下来让我们实现下:
type mypick<t, keys> = { [key in keys]: t[key] // error: type 'key' cannot be used to index type 't'. }
如果你直接这么写,ts
编译器肯定是要报错的。因为用户传入的属性集合中很可能在 t
中不存在!
所以我们需要对属性集合 keys
进行约束,约束其必须为 t
的属性集合子集。
type mypick<t, keys extends keyof t> = { [key in keys]: t[key] } // 使用 interface todo { title: string; description: string; completed: boolean; } type todopreview = mypick<todo, "title" | "completed">; const todo: todopreview = { title: "clean room", completed: false, }; todo; // ok
这是第二种方式,对泛型参数进行约束。
extends 实现条件类型判断
在 typescript
类型体操基础动作中,有一种动作叫:条件类型,条件类型主要用于去判断两个类型之间的关系。
建议阅读
比如工具类型 exclude
的实现,就是基于条件类型:
type myexclude<t, key> = t extends key ? never : t
可以实现基于联合类型 key
,排除联合类型 t
中匹配的类型。
type t0 = myexclude<"a" | "b" | "c", "a">; // type t0 = "b" | "c" type t1 = myexclude<"a" | "b" | "c", "a" | "b">; // type t1 = "c"
在多数工具类型中,都用到了这个特性,最常见的就是递归类型。递归三要素之一就是要有终止条件,而我们就可以通过 extends
实现终止条件的判断。
比如
- 实现一个 deepreadonly 工具类型,可以做到将对象类型的所有属性转为只读:
type deepreadonly<t> = keyof t extends never ? t : { readonly [key in keyof t]: deepreadonly<t[key]> : t[key] }
- 实现一个 trimleft ,可以实现移除字符串类型的左边空格:
type space = ' ' | '\n' | '\t' type trimleft<s extends string> = s extends `${space}${infer r}` ? trimleft<r> : s type trimed = trimleft<' hello world '> // expected to be 'hello world '
- 实现一个 kebabcase 类型,可以实现对字符串类型的驼峰转横杠:
type kebabcase<t extends string> = t extends `${infer f}${infer r}` ? r extends uncapitalize<r> ? `${uncapitalize<f>}${kebabcase<r>}` : `${uncapitalize<f>}-${kebabcase<r>}` : t; type foobarbaz = kebabcase<"foobarbaz">; const foobarbaz: foobarbaz = "foo-bar-baz"; type donothing = kebabcase<"do-nothing">; const donothing: donothing = "do-nothing";
是不是很有意思?
最后
恭喜你,通过短短几分钟,有进步了一丢丢。
参考
- typescript:https://www.typescriptlang.org/docs/handbook/2/everyday-types...
- deepreadonly: https://github.com/type-challenges/type-challenges/blob/main/questions/00009-medium-deep-readonly/readme.md
- trimleft: https://github.com/type-challenges/type-challenges/blob/main/questions/00106-medium-trimleft/readme.md
- kebabcase:https://github.com/type-challenges/type-challenges/blob/main/questions/00612-medium-kebabcase/readme.md
以上就是typescript extends 关键字的三个妙用的详细内容,更多关于typescript extends 关键字的资料请关注代码网其它相关文章!
发表评论