TypeScript在项目中高级应用

615 阅读7分钟

除了简单的用冒号去定义一个变量类型, 还有很多神奇操作. 但ts官网教程太庞大了, 我一般都是天马行空地想到需要什么类型,就去问chatGpt, 然后在官网去看看对应的api

总结一下常用的高级用法

联合类型和类型保护

什么是联合类型

当 TS 不确定一个联合类型的变量到底是哪个类型的时候,可以定义多种类型,例如,一个变量既支持 number 类型,又支持 string 类型

ts.xcatliu.com/basics/unio…

联合类型用 | 表示,如

const a: number | string = 'abc'

类型保护

如果一个变量是联合类型, 那在使用的时候如何判断这个变量具体的类型是什么呢, 有以下几种方法

typeof

基础类型还可以使用typeof来进行类型判断。 当type判断变量为"number", “string”, "boolean"或 "symbol"类型时,接下来的代码中该变量将被视为相关类型。 注意如果type判断变量为除四种外的其他类型,ts将无法识别,也就是该变量仍然被视为原有类型不变。

const a: number | string = 'abc'
const b = a.length // 报错
if(type a === string) {
	const b = a.length // ok
}
const props : number | string  = 1
if(typeof props === 'number'){
    console.log(props/2)
}

instanceof类型保护

用法和typeof相同

in关键字

in类型保护检查对象是否具有特定的属性,并使用该属性区分不同的类型。

等式收缩保护器

function getValues(a: number | string, b: string) {
    if(a === b) {
        // this is where the narrowing takes place. narrowed to string
        console.log(typeof a) // string
    } else {
        // if there is no narrowing, type remains unknown
        console.log(typeof a) // number or string
    }
}
console.log(getValues(2,"2"))

自定义类型保护

可以自己写一个自定义函数,用来判断变量的类型,这个函数返回一个布尔值。 返回值类型中使用 参数 is 类型 做定义。 当返回值为true时,任务参数 is 类型成立,否则不成立。

function isFish(pet: Fish | Bird): pet is Fish {
    return (<Fish>pet).swim !== undefined;
}

上面代码中记录了一个自定义函数isFish,返回值类型为 pet is Fish ,也就是说当返回值为true时,ts认为pet是Fish类型,否则是Bird类型。 自定义函数的使用方式如下:

if (isFish(pet)) {
    pet.swim();
}
else {
    pet.fly();
}

剔除null与undefined

变量在未赋值时可能为null或undefined,这很常见。 但是在使用时就会报错,undefined上不包含该属性。 也就是变量类型为包含null/undefined的联合类型。 当一个变量的联合类型中含有null,可以直接通过判断去掉。

function f(sn: string | null): string {
    if (sn == null) {
        return "default";
    }
    else {
        return sn;
    }
}

为一个变量剔除null和undefined选项,也可以通过!进行快捷断言。

name!.charAt(0)

参考
blog.csdn.net/weixin_4580… bbs.huaweicloud.com/blogs/35288…

交叉类型 &

联合类型 | 是指可以取几种类型中的任意一种,而交叉类型 & 是指把几种类型合并起来。 联合类型 | 是指可以取几种类型中的任意一种,而交叉类型 & 是指把几种类型合并起来。 // 交叉类型

let obj:{name: string, age:number} & {height: number};
obj = {name: 'll', age: 19, height: 20} // 属性必须全部要有,少了一个都报错

复用type定义的类型

type Point = {
  x: number;
  y: number;
};

type Coordinate = Point & {
  z: number;
};

复用interface定义的类型

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

interface Coordinate extends Point {
  z: number;
}

interface复用type定义的类型

type Point = {
  x: number;
  y: number;
};

interface Coordinate extends Point {
  z: number;
}

type复用interface定义的类型

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

type Coordinate = Point & {
  z: number;
};

Pick 挑选出指定属性,生成新对象类型

type UserInfo = Pick<Person, 'name' | 'age'>; // 挑选出 { name: string; age: number }

Omit 排除指定的属性,生成新的对象类型

type UserInfo2 = Omit<Person, 'id'>; // 排除 id,生成  { name: string; age: number }

Exclude 排除一些联合类型

type UserInfoKeys = Exclude<keyof Person, 'id'>; // 'name' | 'age'

Partial 将对象所有属性变为可选

type PartialPerson = Partial<Person>; // { name?: string; age?: number; id?: number }

Readonly 将对象所有属性变为只读

type ReadonlyPerson = Readonly<Person>; // { readonly name: string; readonly age: number; readonly id: number }

Record 生成对象类型,例如

type PersonMap = Record<number, Person>; // { [index: number]: Person }

枚举

普通枚举和常量枚举的区别

ts中普通枚举和常量枚举有什么区别

// 普通枚举
enum Color { Red, Green, Blue }

image.png

打印上面的Color,如下

{
	"0": "Red",
	"1": "Green",
	"2": "Blue",
	"Red": 0,
	"Green": 1,
	"Blue": 2
}

常量枚举:就是普通枚举前面加const,但编辑后枚举不见了,如果不使用枚举值就不会生成任何相关代码

image.png

总结:

  • 普通枚举 enum A {...} 和常量枚举 const enum A {...} 之间的区别主要在于 TS 的编译结果上有所差别

  • 普通枚举 enum A {...}, 会将其编译为一个 JS 对象, 对象内就是枚举成员和值的一个相互映射

  • 常量枚举 const enum A {...}, 编译后不会生成任何代码, 会删除 TS 部分内容, 对于使用到的成员只会进行值的替换

  • 普通枚举适用于需要动态修改枚举值的情况,‌或者在开发过程中需要根据业务逻辑动态调整枚举值时使用

  • 常量枚举适用于需要确保枚举值在编译后保持不变的情况,‌比如在编写库或框架时,‌为了确保API的稳定性,‌需要固定枚举值,‌避免在后续开发中意外更改这些值(vite中的env变量就可以看成是常量枚举,因为他编译后就不会变化)

juejin.cn/post/730547…

JavaScript中的四种枚举方式

www.cnblogs.com/chuckQu/p/1…

保护枚举的值不被改变

Object.freeze和Object.seal

freeze 冻结一个对象,注意是浅冻结 seal 封闭一个对象

相同点: 都对变量进行了以下操作

  1. 设置Object.preventExtension(),禁止添加新属性(绝对存在)
  2. 设置configurable为false,禁止配置(绝对存在)
  3. 禁止更改访问器属性(getter和setter)

不同点: freeze多了一步操作:

  1. 设置writable为false,禁止修改(绝对存在)

换句话说,不同的地方如下:

  1. 用Object.freeze()冻结的对象中的现有属性是不可变的。
  2. 用Object.seal()密封的对象可以改变其现有属性。
const obj = {name:'lisa'}
Object.freeze(obj)
console.log(Object.getOwnPropertyDescriptor(obj,'name'))
{
  configurable: false,
  writable: false
  enumerable: true,
  value: "lisa",
}

Object.seal(obj)
{
  configurable: false,
  writable: true
  enumerable: true,
  value: "lisa",
}

config false- write true ->false 遍历修改

Object.preventExtension()

只是禁止添加新的属性

configurable描述符

image.png

Object.freeze()Object.seal(), Object.preventExtensions()

www.cnblogs.com/lxlx1798/ar…

developer.mozilla.org/zh-CN/docs/…

developer.mozilla.org/zhCN/docs/W…
JavaScript 中对象处理之Object.freeze 与 Object.seal

developer.mozilla.org/zh-CN/docs/…

对象的所有key值作为新的类型

To get a type that is a union keys of a variable you need to use keyof typeof variableName.

const MY_OBJECT = {
    'key': 'key val',
    'anotherKey': 'anotherKey val',
};
type MY_OBJECT_KEYS = keyof typeof MY_OBJECT // "key" | "anotherKey"

stackoverflow.com/questions/5…

变量类型设置为interfacte类型中的某个属性的类型

新类型为 ( 其他类型['字段值']

interface Person {
  name: '小米' | '小明'
  age: number
}

interface Student {
  mingzi: Person['name'] 
  // 这个mingzi类型为Person中name的类型
}

stackoverflow.com/questions/7…

枚举作为对象的key类型

export enum colorsEnum{
    red, blue, green
}

export type colorsInterface = {
    [key in colorsEnum]: boolean;
};

let example: colorsInterface = {
    [colorsEnum.red]: true,
    [colorsEnum.blue]: false,
    [colorsEnum.green]: true
};

如果不想使用所有的key,可以加?

export type colorsInterface = {
    [key in colorsEnum]?: boolean;
};

let example: colorsInterface = {
    [colorsEnum.red]: true,
    [colorsEnum.blue]: false
};

stackoverflow.com/questions/3…

keyof 和 type keyof

ts Element implicitly has an any type because expression of type string can't be used to index type 这个错误一般出现获取对象里面的值的情况, 对应的key变量类型没有声明

interface Person {
  name: string,
  age: number,
}
const obj: Person = {
  name: '张三',
  age: 20,
};
 
const str: keyof Person = 'name'; // 另一个类型的key 类型
const str: type keyof obj = 'name'; // 另一个变量的key类型

console.log(obj[str]);

其他方法

类型注解{ [key: string]: any }

在 TypeScript 中,{ [key: string]: any } 表示一个对象,其中键是字符串类型,而值可以是任意类型。

  • {} 表示这是一个对象类型。
  • [key: string] 表示对象的键是字符串类型。 这种语法可以用于定义具有动态属性的对象。
  • : any 表示对象的值可以是任意类型。

箭头函数的返回类型

interface AddTodoAction {
    type: "ADD_TODO",
    text: string
}

export const addTodo3 = (text: string) => <AddTodoAction>({
    type: "ADD_TODO",
    text
})
export const addTodo4 = function(text: string): AddTodoAction {
    return {
        type: "ADD_TODO",
        text
    }
}

export function addTodo5(text: string): AddTodoAction {
    return {
        type: "ADD_TODO",
        text
    }
}
// 箭头函数设置返回值类型, 一般参数类型和返回值类型都是分开定义的,比较灵活 
export const addTodo6 = (text: string): AddTodoAction =>{
    return {
        type: "ADD_TODO",
        text
    }
}

stackoverflow.com/questions/4…

www.typescriptlang.org/docs/handbo…

try..catch..error类型的处理

一般捕获的error在ts中为any类型,可以用instanceof判断处理

class DivisionByZeroError extends Error {}
try {
    // 假设这里有可能会抛出DivisionByZeroError的代码
    const result = 10 / 0;
} catch (error) {
    if (error instanceof DivisionByZeroError) {
        console.error("Cannot divide by zero!");
    } else {
        // 处理其他类型的错误
    }
}

也可以直接以error类型处理

try {
  
} catch (error as any as Error) {
    
}

stackoverflow.com/questions/5…

命名空间也可以看看
www.typescriptlang.org/docs/handbo…

其他参考
stackoverflow.com/questions/4… www.cnblogs.com/ygyy/p/1819…

TS教程-小白也能秒懂的TS入门指南,跟上程序员的脚步!

react 相关typescript
stackoverflow.com/questions/6…
stackoverflow.com/questions/7… stackoverflow.com/questions/7…