与typeScript较劲的一周

50 阅读11分钟

Typescript

- 基本类型

null/undefined

const a:null = null;
 a = 1 // error 一旦定义了null、undfined就不能再赋其他值

string

const a:string = '1';

number

const a:number = 1;

boolean

const a:boolean = false

symbol

const a:symbol = Symbol('test'); // 声明Symbol类型
const b:object = {[test]: '111'}; // 注意对象名称有[]
console.log(b[Object(a)]) // 打印111

bigint

number类型 范围:-9007199254740991 (-(2^53-1))` 和`9007199254740991(2^53-1),超过区间精度会丢失,bigInt类型可以解决精度丢失的问题
用法与number类似

- 引用类型

Array

有两种声明数组的方式
①Array<number0> 栗子:const a:Array<number> = [1,2,3]
②number[] 栗子:const a:number[] = [1,2,3]

Object

const obj:{a: number,b: number} = {a: 1, b: 2}

function

也可以写入返回值的类型,如果写入,则必须要有对应类型的返回值,**但通常情况下是省略**,因为`TS`的类型推断功能够正确推断出返回值类型
function写法:
const setObj(name: string):string { // ()里是传入的参数类型 :是函数输出的类型
    return name
}
箭头函数写法:
const setObj = (name: string, age?:number): void => { // ()里是传入的参数类型 :是函数输出的类型
    return name
}

- 函数重载

函数重载是使用相同名称和不同参数数量或类型创建多个方法的一种能力。在TypeScript中,表现为给同一个函数提供多个函数类型定义。

let obj = {}
function updateObj (val: number | string | boolean) {
    if(typeOf(val) == 'string'){
        obj.a = val
    }else{
        obj.b = val
    }
}
updateObj('11')
updateObj(11)
console.log(obj) // {a: '11', b: 11}

- 特殊类型

  • any类型:任何类型都可以归于any类型,所以any类型成为了所有类型的[顶级类型],同时[如不指定变量的类型,则默认为any类型],当然不推荐这种类型,因为这样丧失了ts的作用

  • unknow类型:与any一样,unknow也可以作为所有类型的顶级类型,但unknow相对严格,主要严格在以下两点:

    1. unknow类型会对值进行检测,而any不会进行检测操作,简言之:any可以被任何类型赋值,unknow只能被unknow和any赋值
    2. unkonw不允许定义的值有任何操作(如方法[u()]、new [new u()]等),但any可以

- void

当一个函数没有返回值时,默认返回void

const objFun: void = () => {} // ok
const objFun: void = () => {return 1} // error
const objFun: void = () => {return undefined} // error

- never

表示一个函数永远不存在返回值,ts类型会认为never,那么与void相比,never应该是void的子级,因为实际上void返回值为undefined,而never连undefined也不行。 符合never的情况有抛出异常和无限死循环

let error =():never => {
    throw new Error('error')
}
let error1 =():never => {
    while(true) {}
}

- Enum(枚举)

可以定义一些带名字的常量,这样可以[清晰表达意图][创建一组有区别的用例],注意:枚举的类型只能是string或number,定义的名称不能为关键字。

  1. 数字枚举(枚组的类型默认为[数字类型],默认从0开始以此累加,如果有设置默认值,则[只会对下面的值产生影响]。同时支持[反向映射],及从成员值到成员名的映射,但智能映射无默认值的情况,并且只能事默认值的前面)
enum numberType {
  A,
  B,
  C = 7,
  D,
}

console.log(numberType.A);  // 0 
console.log(numberType.D);  // 8
console.log(numberType[0]); // A
console.log(numberType[1]); // B
console.log(numberType[2]); // undefined
console.log(numberType[3]); // undefined
  1. 字符串枚举(字符串枚举要注意的是必须要有[默认值],不支持[反向映射]
enum stringType {
  A = "A",
  B = "B",
  C = 7,
  D,
}

console.log(stringType.A); // A
console.log(stringType.D); // 8
console.log(stringType[0]); // undefined
console.log(stringType[1]); // undefined
console.log(stringType[2]); // undefined
console.log(stringType[3]); // undefined

3.常量枚举(除了数字类型和字符串类型之外,还有一种特殊的类型,那就是[常量枚举],也就是通过const去定义enum,但这种类型不会编译成任何JS,只会编译对应的值)

const enum numberType {
    A,
    B,
    C = 7,
    D
}
console.log(numberType.A) // 0
console.log(numberType.C) // 7

4.异构枚举(包含了数字类型字符串类型的混合,反向映射一样的道理)

enum numAndStrType {
  A,
  B,
  C = "C",
  D = "D",
  E = 7,
  F,
}
console.log(numAndStrType.A); // 0
console.log(numAndStrType.F); // 8
console.log(numAndStrType[0]); // undefined
console.log(numAndStrType[5]); // undefined

- 类型推论

在TS中如果不设置类型,并且不进行赋值时,将会推论为[any]类型,如果进行赋值就会默认类型;

- 字面量类型

字符串数字布尔

- 交叉类型(&)

将多个类型合并为一个类型,使用&符号连接如:


export interface item1Type {
  a: string;
}
export interface item2Type {
  b: number;
}
const c: item1Type & item2Type = {a: '1', b: 2}
或
const allType = item1Type & item2Type
const c: allType = {a: '1', b: 2}

同名基础属性合并

如果两个属性都有相同的属性值,那么此时的类型会怎么样?


export interface item1Type {
  a: string;
  b: number;
}
export interface item2Type {
  b: string;
  c: string;
}
const c: item1Type & item2Type = { a: "1", b: 1, c: "2" }; 
// error (property) b: never,不能将类型“number”分配给类型“never”。ts(2322)`item1Type``b`属性类型为`number``item2Type``b`属性为`string`,
合并后b属性类型应该为`number & string`, 但实际上没有这种类型,所以合并后推导出`b`的属性为`never`

- 同名非基础属性合并

export interface item1Type {
  a: string;
}
export interface item2Type {
  b: number;
}
export interface item3Type {
  x: item1Type
}
export interface item4Type {
  x: item2Type
}
const c: item3Type & item4Type = { x: {a: '1', b: 1} }; // 正常合并
如果 接口`item1Type` 中的 也是 `b`,类型为`number`,就会跟`同名基础属性合并`一样

-TS断言和类型守卫

-TS断言

分为三种类型断言、非空断言、确定赋值断言,当断言失效后,可能使用到:双重断言

- 类型断言

在特定环境中,我们会比TS知道这个值具体是什么类型,不需要TS去判断,简单的理解就是[类型断言会告诉编译器,你不用给我进行检查,相信我,他就是这个类型],共两种方式

  • [尖括号]
  • [as] : 推荐
// 尖括号写法
const a = 'xixi'
const b: number = (<string>a).length // react中会error

// as写法
const a = 'xixi'
const b: number = (a as string).length

但需要注意的是:`尖括号语法``[React]`中会报错,原因是与`JSX语法`会产生冲突,所以只能使用`[as语法]`

- 非空断言

在上下文中当类型检查器无法断定类型时,一个新的后缀表达操作符可以用于断言操作对象是[非null和非undefined类型]

const Info = (name: string | null | undefined) => {
    const str: string = name // error 因为name可能为null或undefined
    const str: string = name! // ok
    console.log(str)
}
Info('西西')  // 西西
Info(null)   // null
我们看出来`!`可以帮助我们过滤nullundefined类型,也就是说,编译器会默认我们只会传来string类型的数据,所以可以成功赋值
但ES5之后!会被移除,所以当传入null的时候,还是会打出null

- 确定赋值断言

在TS2.7版本中引入了确定赋值断言,即允许在实例属性和变量声明后面放置一个!,以告诉TS该属性会被明确赋值

let a!: number;

- 双重断言

export interface Info {
    name: string;
    age: number;
]
const name = "西西" as Info // error 不能把string类型断言为一个接口
const name = "西西" as any as Info // ok

- 类型守卫

类型守卫是可执行运行时检查的一种表达式,用于确保该类型一定在范围内。直白说,类型守卫就是你可以设置多种类型,但默认为某种类型。常用的类型守卫有in关键字typeof关键字instanceof类型谓词(is)

in关键字

用于判断属性是否属于接口

export interface InfoItem{
    name: string;
    age: number;
}
const setinfo(data: InfoItem) => {
    if('age' in InfoItem) {
        // do some things
    }
}

typeof关键字

用于判断基本类型(number、string、boolean)

instanceof关键字

用于判断一个实例是不是构造函数,或使用类的时候

class Name {
    name: string = '西西'
}
class Age extends Name{
    age: number = 7
}
const setInfo = (data: Name) => {
    if(data instanceof Age) {
        // do some things
    }
}

类型谓词(is)

function isNumber (x: any): x is number{
}

- 类型别名、接口

类型别名(type)

type infoProps = string | number
const setInfo = (data: infoProps) => {}

接口(interface)

接口:在面向对象语言中表示行为抽象,也可以用来描述对象的形状。

export interface AA {
    name: string;
    age; number
}

接口 - 对象的形状

对象的形状,对象的属性:可读属性只读属性任意属性

  • 可读属性:当我们定义一个接口时,我们的属性可能不需要全都要,这时就需要[?]来解决
  • 只读属性:用[rendonly]修饰的属性为只读属性,意思时指允许定义,不允许之后进行更改
  • 任意属性:这个属性极为重要,它是可以用作就算没有定义,也可以使用,比如[data: string]:any.比如说我们对组件进行封装,而封装的那个组件并没有导出对应的类型,然而又不想让他报错,这时就可以使用任意属性
interface Props {
    a: string;
    b: number;
    c: boolean;
    d?:number; // 可选属性
    rendonly e:number; // 只读属性
    [f:string]:any; // 任意属性
}

接口 - 继承

与类一样,接口也存在继承属性,也是extends字段

interface nameProps {
    name: string;
}
interface props extends nameProps{
    age: number;
}
const obj: props = {
    name: '西西',
    age: '26'
}

接口 - 函数类型接口

同时可以定义函数和类,加new修饰的是(类),不加new的是(函数)

interface props {
    (data: number): number
}
const info: props = (number: number) => number // 可定义函数
// 定义函数
class A {
    name: string
    consttructor(name: string) {
        this.name = name
    }
}
interface propsClass {
    new(name: string): A
}
const info1 = (fun: propsClass, name: string) => new fun (name) 

type 和 interface的区别

基础数据类型

type和interface都可以定义[对象][函数],type可以定义其他数据类型,如字符串、数字、元组、联合类型等,而interface不行

type A = string // 基本类型
type B = string | number // 联合类型
type C = [number, string] // 元组
const dom = document.createElement('div') // dom元素
type D= typeof dom

扩展

interface可以扩展type,type也可以扩展为interface,但两者实现扩展的方式不同

  1. interface是通过extends实现
  2. type是通过&实现
// interface 拓展 interface
interface A {
    a: string
}
interface B extends A {
    b: string
}
const obj: B = {a: '西西', b: '南南'}

// type扩展type
type C = {a: string}
type D = C & {b: string}
const obj: B = {a: '西西', b: '南南'}

// interface 扩展为 type

重复定义

interface可以多次被定义,并且会进行合并,但type不行

// interface 声明A类型 重定义A类型不会报错
interface A {
    a: string
}
interface A {
    b: number
}
const obj: A = {a: '西西', b: 26}
// type 声明B类型 重定义B类型会报错
type B = {a: string}
type B = {b: string} // error 

- 联合类型(Unoion Types)

联合类型:表示取值可以为多种类型中的一种,未赋值时联合类型上只能访问两个类型共有的属性和方法,如:

const steInfo(name: string | number) => {}
setInfo('西西')
setInfo(7)

- 可辨识联合

可辨识联合:包括三个特点,可辨识、联合类型、类型守卫。这种类型的本质是:结合联合类型和字面量类型的一种类型保护方法。如果一个类型是多个类型的联合类型,且多个类型含有一个公共属性,那么就可以利用这个公共属性,来创建不同的类型保护区块

interface A {
    type: 1,
    name: string
}
inteface B {
    type: 2,
    age: number
}
inteface C {
    type: 3,
    sex: boolean
}
const setInfo = (data: A | B | C) => {
     return data.type // ok ABC都有type属性
     return data.age // error
}
const setInfo1 = (data: A | B | C) => {
    if(data.type === 1) {
        console.log('我的名字是${data.name}')
    }else if(data.type === 2) {
        console.log('我的年龄是${data.age}')
    }else{
        console.log('我的性别是${data.sex}')
    }
}

- 泛型

泛型:是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。也就是说,泛型是允许同一个函数接受不同类型参数的一种模板,与any相比,使用泛型来创建可复用的组件更好,因为泛型会保留参数类型

为什么需要泛型

const callArray = (data: any):any[] => {
  let list = []
  for(let i = 0; i < 3; i++) {
    list.push(data)
  }
  return list
}

上面的案例,我们发现在callArray中传任何类型的参数,返回的数组都是any类型,由于我们不知道传入的数据是什么类型,所以返回的数据也为any的数组,但是我们现在想要的效果是:无论我们传什么类型,都能返回对应的 类型

泛型语法

const callArray = <T,>(data: T): T[] => {
      let list: T[] = [];
      for (let i = 0; i < 3; i++) {
        list.push(data);
      }
      return list;
    };
    const res: string[] = callArray<string>("d"); // ["d", "d", "d"]
    const res1: number[] = callArray<number>(7); // [7, 7, 7]

    type Props = {
      name: string;
      age: number;
    };
    const res3: Props[] = callArray<Props>({ name: "西西", age: 27 }); // [{name: "西西", age: 27},{name: "西西", age: 27},{name: "西西", age: 27}]

我们发现传入的字符串、数字、对象,都能返回对应的类型,从而达到我们的目的。

// 泛型语法
function identity <T>(value: T): T {
      return value
    }

实际上T就是传递的类型,从上述栗子看,这个<T>就是<string>,要注意一点,这个<string>实际上是可以省略的,因为TS具有类型推论,可以自己推断类型

多类型传参

我们有多个未知的类型占位,我们可以自定义任何的字母来表示不同的参数类型

 const callArray = <T, U>(name: T, age: U): {name: T, age: U} => {
      const res: {name: T, age: U} = {name, age};
      return res;
    }
    const res = callArray<string, number>('西西', 7)
    console.log(res, "ressssssssssss") // {name: '西西', age: 7}

泛型接口

定义接口时,我们也可以使用泛型

 interface A<T> {
      data: T;
      name?: string;
      age?: number;
    }
    const info: A<string> = { data: "1", age: 28, name: "西西" };
    console.log(info.age, "ressssssssss"); // 28

泛型类

class callArray<T>{
      private arr: T[] = [];
      add(value: T) {
        this.arr.push(value);
      }
      getValue(): T {
        let res = this.arr[0]
        console.log(res, "ressssssssss");
        return res;
      }
    }
    const res = new callArray()
    res.add(1)
    res.add(2)
    res.add(3)

    res.getValue() // [1, 2, 3]

泛型类型别名

 type info<T> = {
      name?: T;
      age?: T;
    }
    const res: info<string> = {name: '西西'}
    const res1: info<number> = {age: 28}

泛型默认参数

默认参数是指指定类型,当无法推断出类型时,默认类型会起作用

const callArray = <T = string,>(data: T): T[] => {
      let list: T[] = []
      for(let i = 0; i < 3; i++) {
        list.push(data)
      }
      return list
    }

泛型常用字母

  • T: 代表[Type],定义泛型时通常用作第一个类型变量名称
  • K: 代表[Key],表示对象中的键类型
  • V: 代表[Value],表示对象中的值类型
  • E: 代表[Element],表示元素类型