TypeScript 实战技巧

Easter79
• 阅读 703

转自 TypeScript 实践与技巧  https://juejin.im/post/6873080212675166215

一、常用技巧

1. 函数重载

    export function util(str: string): string    export function util(str: number): number    export function util(str: string | number): number | string {      return str;    }        const a: string = util('a');    const b: number = util(1);

2. 捕获变量类型

通过 typeof 关键字捕获变量的类型

    let a = 'abc';    let b = { x: 0, y: 1 };        type A = typeof a; // string 类型    type B = typeof b; // { x: number, y: number }

3 . 函数绑定 this 上下文

通过绑定函数的第一个参数 this 指定当前函数的上下文([官方文档][Link 1])

    const obj = {      say(name: string) {        console.log('Hello,', name);      }    }        // 通过第一个参数 this 绑定当前函数上下文    function foo(this: typeof obj, str: string) {      this.say(str);    }

4. 使用 Never

Never 表示永远不会发生的类型([官方文档][Link 2]),比如抛错函数的返回、死循环函数的返回

    // 抛出异常    function unexpected(): never {      throw Error('Unexpected');    }        function test(x: boolean): number {      if (x) {        return 1;      }      unexpected(); // 因为 never 表示永远不会发生的类型,所以这里不会要求 number 作为返回值    }        // 官方文档给的死循环的例子    function infiniteLoop(): never {      while (true) {}    }

5. 索引签名

    interface A {      x: number    }    const a: A = {x: 1}        // 接口键类型为: string, 值类型为: boolean    interface B {      [key: string]: boolean    }    const b: B = {      a: true,      b: false    }        // in 表示遍历,键名称包括: 'a', 'b', 'c',值类型为: string    type C = {      [key in 'a' | 'b' | 'c']: string    }    const c: C = {      a: '1',      b: '2',      c: '3'    }

6. 通过索引访问类型

([官方文档][Link 3])

    interface A {      a: string      b: {        c: boolean      }    }        type Aa = A['a'] // string 类型    type Abc = A['b']['c'] // boolean 类型    type B = [string, number]    type B0 = B['0'] // string 类型    type B1 = B['1'] // number 类型

7. 使用 as

可以通过 as 关键字指定值的类型,可以很轻松的解决掉一些有歧义的场景

    function foo(str?: string) {      return (str as string).toString();    }        const obj = {      toString() {      }    }    foo(obj as string)

8. 条件类型

顾名思义条件类型就是根据条件决定使用哪种类型([官方文档][Link 4]),条件类型大概长这样:T extends U ? X : Y

    // 与 JS 的三元表达式很类似,如果 T 的类型是 string, 则返回 string 类型,否则,返回 number 类型    type F<T> = T extends string ? string : number        const a: F<string> = 'abc'; // 泛型的类型是 string, 则为 string 类型    const b: F<number> = 1; // 泛型的类型是非 string, 则为 number 类型    const c: F<boolean> = 2; // 泛型的类型是非 string, 则为 number 类型

二、泛型

泛型可以提供更灵活的类型约束,而不局限于某种类型。常用于 functionclassinterfacetype ([官方文档][Link 5])

1. 使用示例

  • function

        function foo(arg: T): T {      return arg;    }

  • class

        class Foo {      name: T          constructor(name: T) {        this.name = name;      }          getName(): T {        return this.name      }    }        const foo = new Foo(3);    const name: number = foo.getName();

  • interface

        interface Foo {      a: T    }        const foo: Foo = { a: 1 }type    type Foo = { a: T }    const foo: Foo = { a: false }

2. 泛型约束

可以通过 extends 关键字约束泛型

    // 指定泛型的类型限制于: string | number, 其他类型将会报错    interface A<T extends string | number> {      [key: string]: T    }        const a: A<string> = {a: 'abc'}    const b: A<number> = {a: 123}

3. 指定泛型默认类型

    interface B<T = number> {      [key: string]: T    }        const c: B = { a: 123 }    const d: B<string> = { a: 'abc' }

三、TypeScript 中的一些运算符

下面梳理了 TypeScript 中的一些运算符,对理解后面的内容非常有帮助

  • ?: 表示可选的参数或属性,可以用于函数参数、接口的属性等

  • -?: 取消可选标识(扩展:类似的还有 -readonly 表示取消 readonly 标识)

  • in: 表示遍历类型

  • typeof: 捕获变量类型,上面有例子

  • keyof: 将对象类型生成其键的字符串或数字的集合,常与 in 结合使用

  • infer: 表示推断类型,下面讲 TS 内置类型 Parameters 实现的时候会有具体例子

四、实现 TypeScript 内置类型

TypeScript 提供了一些常用的类型,开发者可以直接使用它们,下面看看这些类型是如何实现的([内置类型使用文档][Link 6])

1. Record

构造一个键为 K, 值为 T 的键值对类型

    // keyof any 包含: string | number | symbol    type Record<K extends keyof any, T> = {      // 表示键类型是约束于 K (string | number | symbol) 中的一种或多种,值类型为 T      // in 表示遍历      [P in K]: T    }        const foo: Record<string, boolean> = {      a: true    }    const bar: Record<'x' | 'y', number> = {      x: 1,      y: 2    }

2. Partial

使 T 中的所有属性都变成可选的

    type Partial<T> = {      // 将原始类型 T 的所有属性加上 ? 修饰,变成可选的      [P in keyof T]?: T[P]    }        interface Foo {      a: string      b: number    }        const foo: Partial<Foo> = {      b: 2 // `a` 已经不是必须的了    }

3. Required

与 Partial 相反,将 T 中的所有属性变成必须的

    type Required<T> = {      // 将原始类型 T 的所有属性加上 -? 修饰,变成必须的      // -? 表示移出 ? 这个标识      [P in keyof T]-?: T[P]    }        interface Foo {      a: string      b?: number    }        const foo: Required<Foo> = {      a: 'abc',      b: 2 // `b` 已经变成必选的了    }

4. Readonly

使 T 中的所有属性变成只读

    type Readonly<T> = {      // 将原始类型 T 的所有属性加上 readonly 修饰,属性变成只读的      readonly [P in keyof T]: T[P]    }        interface Foo {      a: string    }        const foo: Readonly<Foo> = {      a: 'abc'    }    // foo.a = 'def' // 只读,不可修改            // 此外,可以使用 -readonly 修饰,表示去掉 readonly 修饰    type Writable<T> = {      -readonly [P in keyof T]: T[P]    }        interface Bar {      readonly a: string    }        const bar: Writable<Bar> = {      a: 'abc'    }    bar.a = 'def'; // `Bar['a'] 已经去掉了 readonly 修饰,可以修改了

5. Pick

从类型 T 中选择一些属性,这些属性来自于集合 K

    type Pick<T, K extends keyof T> = {      [P in K]: T[P]    }        interface Foo {      a: string      b: number      c: boolean    }        const foo: Pick<Foo, 'b' | 'c'> = {      b: 1,      c: false    }

6. Exclude

排除掉 T 中能赋值给 U 的类型

    // 如果 T 是 U 的子类型,则返回 never, 否则返回 T    type Exclude<T, U> = T extends U ? never : T        // 'a' | 'b' | 'c' 中排除掉 'b', 只能为 'a' 或 'c'    let foo: Exclude<'a' | 'b' | 'c', 'b'> = 'a'    foo = 'c'

7. Extract

与 Exclude 相反,提取 T 中能赋值给 U 的类型

    // 如果 T 是 U 的子类型,则返回 T, 否则返回 never    type Extract<T, U> = T extends U ? T : never        // 'a' | 'b' | 'c' 中提取 'b', 只能为 'b'    let foo: Extract<'a' | 'b' | 'c', 'b'> = 'b'

8. Omit

省略掉 T 中的一些属性,这些属性来自于集合 K

    // 1. 从 T 的属性中排除掉 K,得到剩下的一个属性集合    // 2. 从 T 中选择剩下的集合    type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>        interface Foo {      a: string      b: number      c: boolean    }        // 从 Foo 中省略 'c', 接口 Foo 只剩下 'a' 和 'b'    let foo: Omit<Foo, 'c'> = {      a: 'a',      b: 1    }

9. NonNullable

排除掉 null 、undefined 类型

    // 排除掉 null | undefined    type NonNullable<T> = T extends null | undefined ? never : T        type Foo = string | null    const a: NonNullable<Foo> = 'a' // 不能赋值给 null 或 undefined

10. Parameters

根据函数的参数,返回对应的元组类型

    // 主要说一下 infer P, 在这里表示待推断的函数参数    type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never        type Foo = (a: string, b: number) => void    const a: Parameters<Foo> = ['a', 1] // 元组 [string, number]

11. ConstructorParameters

根据构造函数的参数,返回对应的元组类型

    // 跟 Parameters 类似    type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never        interface Foo {      new(a: string, b: number)    }        const a: ConstructorParameters<Foo> = ['a', 1] // 元组 [string, number]

12. ReturnType

返回函数的返回类型

    // 跟 Parameters 类似    type ReturnType<T extends (...args: any) => any> = T extends (...args:any) => infer R ? R : any        type Foo = () => boolean    const a: ReturnType<Foo> = true // 返回 boolean 类型

13. InstanceType

    // 跟 Parameters 类似    type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any

参考链接

  • [TypeScript 手册参考][TypeScript]

  • [深入理解 TypeScript][TypeScript 1]

  • [TS 中的内置类型简述][TS]

本文分享自微信公众号 - JavaScript忍者秘籍(js-obok)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
2年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
JS 对象数组Array 根据对象object key的值排序sort,很风骚哦
有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak
Stella981 Stella981
2年前
HIVE 时间操作函数
日期函数UNIX时间戳转日期函数: from\_unixtime语法:   from\_unixtime(bigint unixtime\, string format\)返回值: string说明: 转化UNIX时间戳(从19700101 00:00:00 UTC到指定时间的秒数)到当前时区的时间格式举例:hive   selec
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
JavaScript常用函数
1\.字符串长度截取functioncutstr(str,len){vartemp,icount0,patrn/^\x00\xff/,strre"";for(vari
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k