【TypeScript】入门 + 进阶( 配合React使用)

3,746 阅读21分钟

今年是特殊的一年,回京后因疫情被困家中半月之久,现在虽已复工,但每日都需全天佩戴口罩。大家也保护好自己呦,待到山花烂漫时,再好好出门嗨吧!言归正传,由于年前业务线上过于忙碌😂,所以拖到上周才排上日程,此文也作为一个自己的学习笔记,分享给各位小白同学,文中如有错误之处,请帮忙留言给我哦!

一、 关于Typescript

    TypeScript 是 JavaScript 的一个超集,主要提供了类型系统和对 ES6 的支持,
    
    它由 Microsoft 开发,代码开源于 GitHub 上。
    
    它的第一个版本发布于2012年10月,经历了多次更新后,现在已成为前端社区中不可忽视的力量,
    
    不仅在 Microsoft内部得到广泛运用,而且 Google 的 Angular2 也使用了 TypeScript 作为开发语言。
                                                               ——阮一峰

简单的说,Typescript就是类型严格的Javascript,是运行在前端的Java


1. 为什么会有ts的出现

因为js是一门弱类型语言,变量的数据类型具有动态性,自由度过高,只有执行时才能确定变量的类型,而ts在定义时就明确了数据的类型,再配合vscode的插件配置,真正实现了静态检查功能,简直不要太开心喽!😄


2. ts有哪些具体的优势

  1. 类型定义,使代码更规范

这一点是ts的精髓了,在创建变量 / 函数 / 接口,就定义好了数据的具体类型,可以在编译阶段就发现大部分错误,就不用每次都依赖运行来检查错误了。

  1. 增加了代码的可读性和可维护性

这一点是基于上一点的延伸与扩展,开发中程序员最头疼的事情是什么?接手的项目没文档,没注释,那就只能抓耳挠腮的反复理解,现在有了ts,类型系统实际上是最好的文档,直接清晰的标注:哪些是必填,哪些是可选,以及具体的数据类型。

  1. 强大的包容性

tsjs 的超集,.js 文件可以直接重命名为 .ts

二、基础类型

类型列表:

anystringnumberboolean、arr、tuple、enumvoidneverundefined && null

1. any任意类型

刚接触ts的朋友们,肯定最熟悉的就是这个any了,在ts中的any是默认的类型,其类型的变量允许任何类型的值。如果你把一个变量定义为any那就和写js没什么区别了,业内称之为anyscript,哈哈 😄

const name:any = '张三';

const age:any = 18;

const isbool:any = true;

const arr:any[] = [1,2,'3',true];
                    ....

注意⚠️:既然我们已经选择使用ts来写代码,那就要慎用any,不然我们为什么要千辛万苦来学ts,你说对不对,当然,如果我们在变量声明的时候,不指定其类型,那么它就会被识别为任意类型

2. unknow类型

any 类型本质上是不建议我们过多使用的,但如果我们使用any 类型,就无法使用Typescript提供但大量保护机制,为了解决any 带来的问题,TypeScript3.0就引入了 unknow类型。

let value: unknown;

value = 100;              // true
value = "Hello World";    // true
value = false;            // true
value = [];               // true
value = {};               // true
value = Math.random;      // true
value = null;             // true
value = undefined;        // true
value = new TypeError();  // true
value = Symbol("type");   // true

就像所有类型都可以赋值给 any,所有类型也都可以赋值给 unknown

unknown 类型只能被赋值给 any 类型和 unknown 类型本身,不可以将类型为 unknown 的值赋值给其他类型的变量,如何理解?看下面示例:

let value: unknown;

// true
let value1: unknown = value;         
let value2: any = value;             // true

 // Error
let value3: boolean = value;         // Error
let value4: number = value;          // Error
let value5: string = value;          // Error
let value6: object = value;          // Error
let value7: any[] = value;           // Error
let value8: Function = value;        // Error

如果一个值被定义为unknow,那么它可以被赋值为其他的任何类型,但如果一个非any,非unknow的其他类型的值,那么它是不可赋值给unknow的类型的值

3. string字符串

存储字符串的文本数据类型,可以使用双引号("")单引号('')或反单引号(``)来表示字符串。

let name:string = '张三'

let name2:string = `李四`

仍然可以使用模版字符串:

let name: string = 'zhangsan'
let age:number = 18
let sentence: string = `Hello,my name is ${name}.
I'll be ${age + 1} years old next month.`

4. number数字

数字类型,包括二进制 、 十进制 、十六进制都可以用number类型表示。

let age: number = 23
let hexLiteral: number = 0xf00d
let binaryLiteral: number = 0b1010
let octalLiteral: number = 0o744

5. boolean类型

jsboolean一样,只能赋值给truefalse两个值。

let isOpen: boolean = false
let isClose: boolean = true

6. Array数组

Array同js的数组,但定义方式与上面类型有所不同,

(1). 首先类型大写,后可跟一个<>,定义数组内的所包含的数据类型

let arr: Array<any> = [1,'33',true]
let arr1: Array<number> = [1,3,5,6,7]
let arr2: array<string> = ['name']   // error , array=> Array

(2). 直接定义数组内包含的数据类型,后跟一个[ ]

let arr3: number[] = [2,4,6,7]
let arr4: string[] = ['q','a','n','zh']
let arr5: boolean[] = [true,false,true]

如果我们前后定义类型不一致,编辑器就会报错,也就是说后面数组中给的值,必须全部都符合前面定义的类型,编辑器会给出提示

let arr6: Array<number> = ['33',3,5,'day',7] // error
let arr7: number[] = ['2',true,89]  // error
let arr8: boolean[] = ['3',true] // error

如下红线提示:

7. tuple元组

tuple 类似 Array,可以称是严格版的数组,可以根据顺序、数量定义数据类型。

上面我们讲了定义数组,但是定义数组内的数据类型受限制,要么是纯单一类型的数组,要么是任意类型的数组

let arr:Array<any> = ['1','add',true,222]

let arr:Array<number> =[2,4,5,7,8]

上面两种书写方式都太过于受限制,但有时数组中的顺序是已知的,并且我们不希望它改变,这一讲我们来定义一个任意类型的混合模式,tuple就产生了:

let list:[number,string,string,boolean] = [24,'2','data',false]

必须按照已知的顺序来赋值,不允许有一个错误,:

let list2: [boolean, string, number, boolean] = [99, '2', 'data', false] // error

8. enum枚举

enum类型是对js标准数据类型的一个补充,

默认情况下,从0开始为元素编号,顺序累加1

enum Color {
  Red,
  Green,
  Blue
}
let colorName: string = Color[0]
alert(colorName)   // Red

也可以手动指定成员的数值,如果只有第一个元素定义,且定义为数字,也会顺序累加,9、10、11

enum Color {
  Red = 9,
  Green,
  Blue
}
let colorName: number = Color.Blue
alert(colorName)   // 11

或者,全部都指定赋值

enum Color {
  Red = 'red',
  Green = 'green',
  Blue = 3
}
let colorValue: string | number = Color.Blue
alert(colorValue)  // 3

业务中的使用

export enum TabKey {
  VISIT = 'visit',
  ORDER = 'order',
  LIVE = 'live',
  INTENTION = 'intention',
}

interface ITabList {
  tab: string;
  key: TabKey;
}

// tab list
export const tabList: ITabList[] = [
  {
    tab: '意向',
    key: TabKey.INTENTION, 
  },
  {
    tab: '访问',
    key: TabKey.VISIT, 
  },
  {
    tab: '下单',
    key: TabKey.ORDER, 
  },
  {
    tab: '直播间',
    key: TabKey.LIVE, 
  },
];

// 传入TabKey 中指定的值,返回一个tabList 的对象

export const tabConfigMap: Map<TabKey, ITabList> = new Map(tabList.map(item => [item.key, item]));

console.log(tabConfigMap.get(TabKey.INTENTION));    // {tab: "意向用户", key: "intention"}

9. object非原始类型

一直有一个疑问,对于ts 里是否有object这个类型, 一直是存疑的🤔 哈哈:),因为从没使用过。 今天看大佬的小册才发现,是有object这个类型的,但和jsobject 是不同的,对于非原始类型,也就是除了原始类型(numberstringbooleansymbolnullundefined之外的类型),例如: enum Array tuple等 都属于object

  const obj: object = [2, 'q', { a: 2 }];
  console.log(obj);  // [2, 'q', { a: 2 }] 

对于非原始类型的值,并且拿不准最后的返回或者传入的参数类型时,是可以使用objec来定义的。

10. void空值

表示没有任何数据类型,空值,与any正好相反

只有nullundefined 可以赋值给void类型,所以声明一个void类型但变量没什么用

let unusable: void = null
let unusable2: void = undefined

当一个函数没有返回值时,通常就会赋值为void

function foo(): void {
  alert('This is my warning message')
}
foo()

函数一旦被定义为void,那就不能执行return

function foo(): void {
  const name: string = '张三'
  return name
}
foo()

或者说只能返回nullundefined

function add(): void {
  return null
}

11. never不存在

表示那些不存在的值的类型,没有任何类型可以赋值给never,常用于抛出异常或者没有任何返回值

function error(message: string): never {
  throw new Error(message)
}
error('错误信息')

12. undefinednull

这两个类型是任何类型的子类型,(除严格情况)任何类型都可以赋值undefinednull,但本身但用处不是太大,和void类似

let value1: number = null
let value2: string = undefined 
let value3: boolean = null

13. 联合类型

联合类型是基于我们上面类型的一个整合,可以取值为定义中的任何一个,使用 | 来分割每个类型。

我们可以取string或者number中任何一个类型来赋值,但不能是其他类型

let specialValue: string | number = '10'
let specialValue2:string | boolean | number  = [1,2,4] // error

三、函数

函数是JavaScript中的一等公民,下面我们就来看一下ts中怎么来定义函数的类型。

1. 函数返回值类型

在小括号后定义你想得到的数据类型

function foo(): void {
  return null
}

function foo(): string {
  return 'aaa'
}

function foo(): boolean {
  return false
}

2. 函数参数类型

和定义变量相同,直接跟在参数后添加类型即可

function foo(x: string, y: number, z: boolean): void {
  console.log( x , y , z )
}

3. 可选参数

我们在参数名后添加?,即可将其转换为可选参数,下面的示例中,firstName是必传参数,lastName是可选参数

function name(firstName: string, lastName?: string): string {
  return firstName + lastName
}

// 下面两种调用方式都是正确的
name('xiaofeng')           // xiaofeng
name('xiaofeng','zhang')   // xiaofengzhang

注意⚠️:无论有多少个可选参数,可选参数都必须位于必填参数之后

function buildName(firstName: string, lastName: string,age?: number, job?: string): string {
  if (firstName) {
    return firstName + ' ' + lastName
  } else {
    return lastName
  }
}
let tomcat = buildName('Tom', 'Cat')
let tom = buildName(undefined, 'Tom')

4. 默认参数

可以在函数的参数内添加默认值,当对应参数为undefined时,会启用默认参数

function buildNeame(firstName = '汉三', lastName: string): string {
  console.log(firstName + lastName)
}
let res1 = buildNeame('人')             // error 两个参数都为必填参数
let res2 = buildNeame(undefined, '胡')  // 汉三胡

5. 剩余参数

上面当必填参数,默认参数和可选参数都是一对一的关系,但有时,我们不知道会有多少参数传递进来,就如同arguments一样,ts也有剩余参数,用...来接收,可以有多个,也可以一个都没有,最后存在一个数组里。

function name(firstName: string, ...restOfName: string[]): string {
  return firstName + ' ' + restOfName.join(' ')
}

let employeeName = name('Joseph', 'Samuel', 'Lucas', 'MacKinzie') // Joseph Samuel Lucas MacKinzie

四、类 && 继承

相比之前的类,在写法上相同,添加了参数的类型定义和访问权限权限的设置

1. 参数的类型定义

class Person {
  name: string
  age: number
  job: string
  constructor(name: string, age: number, job: string) {
    this.name = name
    this.age = age
    this.job = job
  }
  print() {
    return '姓名:' + this.name + '年龄:' + this.age + '职业:' + this.job
  }
}

let person: Person = new Person('张三', 19, '医生')
person.print()
console.log(person)

2. 访问限定

类中有三个修饰符,可控制参数的访问权限,是公共属性、受保护属性、私有属性

  • public:公共属性,成员默认为public,可加,可不加,都可以被外部访问。
  • protected:受保护属性 ,只可以被类的内部以及类的子类访问
  • private:私有属性,当成员被标记为:private,它只可以被类对内部访问,不能被外部访问,继承中也不可以

如下示例中,name是公共属性,age是私有属性,job是受保护属性

// 父类
class Parent {
  public name: string
  private age: number
  protected job: string
  constructor(name: string, age: number, job: string) {
    this.name = name
    this.age = age
    this.job = job
  }
  // 在自己的类中,这些属性都是可以正常访问的
  info() {
    console.log(this.name + this.age + this.job)
  }
}
// 子类
class Children extends Parent {
  constructor(name: string, age: number, job: string) {
    super(name, age, job)
  }
  // 此处的属性“age”为私有属性,只能在类“Person”中访问,所以this.age无法被访问到
  info() {
    console.log(this.age)
  }
}
let child = new Children('张三', 22, 'ddd')
child.info()

3. 存储器

支持通过gettersetter来截取对属性成员的访问,如果只有get,不带set的存储器会被自动推断为readonly只读状态

class Hello {
  private name: string
  private job: string
  get name(): string {
    return this.name
  }
  set name(value: string) {
    this.name = value
  }
  get job(): string {
    return this.job
  }
}
let hello = new Hello()

// 可以被访问
console.log(hello.name)

// 可以被赋值
hello.name = '王五'

// job只有get,没有set,所以只能被访问
console.log(hello.job)

// 但不能被赋值
// Cannot assign to 'job' because it is a read-only property.ts(2540)
hello.job = 'teacher'

五、接口

ts里,接口的作用就是为这些类型命名和为你的代码或第三方组件定义契约,用关键字interface来定义接口参数的数据类型,并将其给到函数,函数的参数必须遵守interface里面定义的类型

interface params {
  label: string,
  title: string
}
function foo(param: params) {
  console.log(param.label,param.title)
}
foo({label:'名称',title:'啊'})

1. 只读属性readonly

我们的参数可以添加只读属性,这样在后续就不能再次赋值了

interface Point {
  readonly x: number
  readonly y: number
}
let p1: Point = { x: 10, y: 20 }
    p1.x = 1  // error

2. 可选属性 ?

有些情况下,参数是可以不必传的,在属性的后添加一个?号,即可成为可选属性

interface Point {
  x: number
  y?: number
}
let p1: Point = { x: 10 }

3. 函数类型

函数的参数和返回值可以一起通过接口来定义

interface GetInfo {
  (name: string|number, age: number): boolean
}

let getInfo: GetInfo

getInfo = function(name: string, age: number) {
  return typeof(name)===typeof(age)
}

console.log(getInfo('张三', 12))  // false
console.log(getInfo(12, 13))      // true

扩展:在函数类型的检查来说,函数的参数名可以不与接口定义的名字一摸一样,函数的参数会根据顺序进行逐个检查,只要对应位置上的参数类型是正确的即可,如下:

interface GetInfo {
  (name: string|number, age: number): boolean
}

let getInfo: GetInfo
// 此处参数的名称可以不和接口中定义的一致,对应位置的类型正确即可
getInfo = function(label: string, num: number) {
  return typeof(label)===typeof(num)
}

console.log(getInfo('张三', 12)) // false
console.log(getInfo(12, 13)) // true

4. 类-类型

class类来添加类型定义

interface ClockInterface {
    currentTime: Date;
}

class Clock implements ClockInterface {
    currentTime: Date;
    constructor(h: number, m: number) { }
}

5. 接口继承 extends

同类一样,接口也可以相互继承,这样我们就能从一个接口里复制成员到另一个接口中,这样方便我们后期重用。

interface Shape {
    color: string;
}

// 继承 Shape中的color
interface Square extends Shape {
    sideLength: number;
}

let square = <Square>{};

square.color = "blue";

square.sideLength = 10;

我们还可以同时继承多个接口,创建出多个接口的合成接口

interface Shape {
    color: string;
}

interface PenStroke {
    penWidth: number;
}

interface Square extends Shape, PenStroke {
    sideLength: number;
}

let square = <Square>{};

square.color = "blue";

square.sideLength = 10;

square.penWidth = 5.0;

6. 接口继承类

当接口继承了一个类的类型时,它会继承类的成员但不包括其实现。就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样。接口同样会继承到类的privateprotected成员。这意味着当你创建了一个接口继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现(implement)。

class Control {
    private state: any;
}

interface SelectableControl extends Control {
    select(): void;
}

class Button extends Control implements SelectableControl {
    select() { }
}

class TextBox extends Control {
    select() { }
}

// 错误:“Image”类型缺少“state”属性。
class Image implements SelectableControl {
    select() { }
}

六、泛型

这一趴来分析一下关于ts的边界知识点

1. 基础泛型

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

有时我们需要一个函数接收什么类型参数,就返回什么类型的值,这时我们就用到了泛型。我们在函数名后添加 <T>,其中T就用来指代任意输入的类型,前后类型一致。

function foo<T>(value: T): T {
  return value-1
}
foo('3') // error 接收的是string ,返回的是number

2. 多参数泛型

当然,我们的参数不可能总是接收一个参数,返回一个参数,我们多参数的场景也较多,这里分享一下多参数的写法。

// 定义一个arr的参数,类型顺序分别是 [T,U],函数返回值定义为 [U,T]

function resut<T, U>(arr: [T, U]): [U, T] {
    return [arr[1], arr[0]];
}

resut([111, 'aaaa']);   // ['aaaa',111]

当然如果我们返回的值来颠倒一下,就会执行类型报错:

我们把鼠标放在第一个类型报错上,弹出如下提示:

那当我们有3,4,5,6....个标签该怎么传呢?一样的道理,如下

// <>尖括号中的值就相当于一个变量,前后是对应关系,虽然说小写也可以认,但我们尽量都用大写

function resut<T, U, S, K, II, i>(
  arr: [T, U, S, K, II, i]
): [U, T, S, K, II, i] {
  return [arr[1], arr[0], arr[2], arr[3], arr[4], arr[5]]
}

resut([111, 'aaaa', 9, true, 's', 3])

七、类型断言 as

手动去指定一个值的类型,但指定但类型必须属于在声明的类型中,断言成一个联合类型中不存在的类型是不允许的,可以起到代码保护作用,不会使代码崩溃。

有时候你会遇到这样的情况,你会比TypeScript更了解某个值的详细信息。通常这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型。通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么,不管原来是什么类型,现在它就是我指定的类型”。

**类型断言有两种形式。 其一是“尖括号”语法,<type>value **

let someValue: any = "this is a string";

let strLength: number = (<string>someValue).length;

另一个为as语法 value as type

let someValue: any = "this is a string";

let strLength: number = (someValue as string).length;

八、keyof 操作符

TypeScript 允许我们遍历某种类型的属性,并通过 keyof 操作符提取其属性的名称,还可获取到value的值,如下:

1.获取类型的key

interface Point {
  x: number;
  y: number;
}

type keys = keyof Point;     // type keys = "x" | "y"

const value: keys = 'y';     // true

const value2: keys = 'z';    // false
 

2.获取类型的value

export type IPerson = {
  name: string;
  age: number;
  job: string;
};

export const propPick = <T, K extends keyof T>(obj: T, key: K) => {
  return obj[key];
};

const person: IPerson = {
  name: 'xiaoming',
  age: 12,
  job: 'student',
};
const name = propPick(person, 'name');
console.log('name', name); // xiaoming

3.从类型中分离部分类型定义pick

export interface IMapping {
  label: string;
  value: number | string;
}

export const studyState: IMapping[] = [
  {
    label: '全部',
    value: '',
  },
  {
    label: '是',
    value: 1,
  },
  {
    label: '否',
    value: 2,
  },
];

type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

type PickMapping = Pick<IMapping, 'label'>;

export const studyTagConfig: PickMapping[] = [
  {
    label: '否',
  },
  {
    label: '是',
  },
];

4.省略部分指定的类型Omit

interface IPerson {
  name: string;
  age: number;
  job: string;
}

const person: IPerson = {
  name: 'xiaoming',
  age: 12,
  job: 'student',
};

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

// 相当于: type OmitPerson = { age: number; name: string; }
type OmitPerson = Omit<IPerson, 'job'>;

const personPart: OmitPerson = {
  age: 2,
  name: 'zzz',
};

九、预定义条件类型

TypeScript 2.8lib.d.ts里增加了一些预定义的条件类型,方便我们灵活的操作类型

Exclude<T, U>:从 T 中移除掉 U 的子类型。

type C = Exclude<boolean | number, boolean> ;

let c: C =  5 ;  // number

Extract<T, U>T 中筛选出 U 的子类型。

type C = Extract<string | number, string> ;

let c:C = '1' // string

NonNullable<T>:从 T 中移除nullundefined

type C = NonNullable<string | number | null | undefined> ;

let c: C = '1' // string | number

ReturnType<T>:获取函数返回值类型。

function getUserInfo() {

    return { 
    
        name: '金色小芝麻', age: 10 ;
        
        } 
} 
    
type UserInfo = ReturnType<typeof getUserInfo> ;

let user: UserInfo = { name: '金色小芝麻', age: 10 }  // {name: string;age: number;}

InstanceType<T>:获取构造函数的实例类型。

class Person {

  name: string
  
  constructor(name: string) {
  
    this.name = name;
    
  }
}

type P = InstanceType<typeof Person>;

let p: P = new Person('1')   // Person

部分参考链接

十、ts的一些好的使用实践

本部分将分享一下自己在实践中来解决类型问题的具体实践

1. 传入指定key值,返回整个对象或对象中的指定值

export const studyState: IMapping[] = [
  {
    label: '全部',
    value: 0,
  },
  {
    label: '是',
    value: 1,
  },
  {
    label: '否',
    value: 2,
  },
];


export const studyTagConfig: Map<number | string, IMapping> = new Map(
  studyState.map(item => [item.value, item]),
);

// 调用:

 studyTagConfig.get(1).label // 是

如果只想返回label的话,可以改一下返回值:

export const studyTagConfig: Map<number | string, string> = new Map(
  studyState.map(item => [item.value, item.lable]),
);

// 调用:

 studyTagConfig.get(1) // 是
 

2. 类型联合

某些时候我们定义了A类型和B类型,但这是又出现了C类型,C类型是A类型和B类型的合并,那就直接使用A + B合体即可,不需要重新定义。

A 类型:


interface IPerson {
  name: string;
  age: number;
  job: string;
}

B 类型:

  interface IInfo {
    key: number;
    text: string;
  }

C 联合类型:

type IParams = IInfo & IPerson

const params: IParams = {
    name: 'xiaoming',
    age: 12,
    job: 'student',
    key: 22,
    text: '测试',
  };

十、TypeScript + React Hooks

项目于前几日进行了升级,原本React的项目,添加了tseslint校验规则,原来正常的页面,点开后就 哗哗哗 ☔️的报错,所以,结合React Hooksts也要学起来了,据说是溜的起飞,哈哈😄 看看我能不能飞起来!

注意:react 已经大力推行hooks函数组件的形式书写,所以后文所用到的,都是hooks 的写法哦!

0. 关于函数式组件Hook

HookReact 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

首先,请不用担心,原有的class类组件仍然在维护中。 链接地址

hooks的优点:

  1. 不用再考虑this的问题,全都可以直接拿来就用
  2. 生命周期的合并,考虑的可以更少

关于hooks的更多使用,请参阅:地址

1.propsstate 的类型定义

我们先从最上到下,从propsstate开始讲起,看看在react中如何定义propsstate的类型

代码如下:

import React, { useState } from 'react';
import { Button, Typography } from 'antd';

interface IProps {
  isLoading: boolean;
  dataList: any[];
}

const App: React.FC<IProps> = ({ isLoading, dataList }) => {
  const [data, setData] = useState<string>('张三');

  const handleClick = () => {
    setData('李四');
    console.log('按钮点击');
    console.log(isLoading, dataList);
  };

  return (
    <>
      <Button onClick={handleClick}>按钮</Button>
      <Typography.Text> {data}</Typography.Text>
    </>
  );
};

export default App;

2. 分解React中的TS类型定义

下面是我工作中比较常用的类型定义,然后我们逐一将其分解

import React from 'react';
import { PaginationProps } from 'antd/es/pagination';

interface IResponseUpload {
  Location: string;
}

interface Item {
  label: string;
  value: number;
}

interface IStatusDict {
  [key: number]: {
    text?: string;
    color?: string;
  };
}

interface IProps {
  name: string;

  age: number;

  isLoading: boolean;

  dataList: Item[];

  statusDict: IStatusDict;

  subIdList: number[];

  detail: {
    job: string;
    showPic?: boolean;
  };

  pagination: PaginationProps;

  type: 1 | 2 | 3;

  size?: 'sm' | 'md' | 'lg' | 'mini';

  width?: number;

  children?: React.ReactChild;

  header?: React.ReactNode;

  onChange?: (e: React.ChangeEvent | string) => void;

  onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;

  onTableFetch: (props: any) => { items: Array<any>; total: number; page: number; size: number };

  onUpload: (file: File) => Promise<IResponseUpload>;
}


a. 无嵌套的基础类型定义

 name: string;  // eg: 'aaa'

 age: number;   // eg:  99

 isLoading: boolean; // eg: false / true

b. 数组、对象有嵌套的数据类型定义

数组

  1. 数组中每一项的类型都相同:
subIdList: number[];   // eg:[1,4,6,3,7]

nameList: string[];     // eg:['a','b','i','p']

isLoading: boolean[];   // eg:[false,false,true,false,true]
  1. 当数组中包含的是对象,我们需要将其拆开
interface Item {
  label: string;
  value: number;
}

对应此类型的数据:

const list:  Item[] = [
  { label: 'tab1', value: 1 },
  { label: 'tab2', value: 2 },
  { label: 'tab3', value: 3 },
];

对象

1.常规对象

 interface IDetail{
    detail: {
        job: string;
        showPic?: boolean;
  };
 }

对应的数据:

const detailData: IDetail = {
  detail: {
    job: 'teacher',
    showPic: false,
  },
};
  1. 类数组的对象 当我们遇到类数组的对象时,我们同样得考虑将其拆出来
interface IStatusDict {
  [key: number]: {
    text?: string;
    color?: string;
  };
}

对应的数据:

const statusDict: IStatusDict = {
  0: {
    text: '未招募',
    color: 'green',
  },
  1: {
    text: '招募中',
    color: 'blue',
  },
  2: {
    text: '未开播',
    color: '#777777',
  },
  3: {
    text: '开播中',
    color: 'orange',
  },
  4: {
    text: '已结束',
    color: '#DDDDDD',
  },
};

c. antd组件自带的类型定义,直接引用

ant.design的组件都有自己定义的类型,给组件的参数定义类型,想知道具体类型的参数名,我们可以查看它的类型定义,直接引用它的类型即可,如下:

import { PaginationProps } from 'antd/es/pagination';
import { FormComponentProps } from 'antd/es/form';

interface IProps{
    pagination: PaginationProps;
    fromProps: FormComponentProps;
}

d. 枚举定义我们的参数范围

我们的参数有时是有一个明确的范围的,这时我们可以给定一个枚举范围,只能在我们给出的类型中选择。

interface IProps{
    type: 1 | 2 | 3;

    size?: 'sm' | 'md' | 'lg' | 'mini';
}

e. React内部的组件、文本定义

react内部也给定义了一部分类型定义,方便我们来使用

interface IProps{
    name: React.ReactText;
    
    children?: React.ReactChild;

    header?: React.ReactNode;
}

  • React.ReactText: 包含stringnumber

  • React.ReactChild: 包含ReactTextReact组件

  • React.ReactNode:包含 ReactChildReactFragmentReactPortalbooleannullundefined;

f. 事件的类型定义

interface IProps{
    onChange?: (e: React.ChangeEvent | string) => void;

    onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;

    onTableFetch: (props: any) => { items: Array<any>; total: number; page: number; size: number };

    onUpload: (file: File) => Promise<IResponseUpload>;
}

react在事件定义中同样也定义很多类型,我们直接引用即可,下面截取了一部分

客官加个收藏,点个赞,记得回来啊,此处内容正在快马加鞭补充中哦.....


写到此处,并没有结束哦!接下来我还会持续追加,看文章的小伙伴们可以添加一下关注哦!

如果你对我对文章感兴趣或者有些建议想说给我听👂,也可以添加一下微信哦!

邮箱:christine_lxq@sina.com

如果亲感觉我的文章还不错的话,可以一下添加关注哦!

最后:
        祝各位工作顺利!
                        -Christine