TypeScript系列:第六篇 - 编写高质量的TS类型

掌握这些,ts类型声明事半功倍 💪🏻

不要做

  • 永远不要使用类型 NumberStringBooleanSymbolObject 这些类型指的是非原始装箱对象,使用 numberstringbooleansymbol 类型
  • 不要使用 any 作为类型,除非正在将 JavaScript 项目迁移到 TypeScript
  • 不要将返回类型 any 用于其值将被忽略的回调,使用返回类型 void,其可以防止意外地以未经检查的方式使用函数的返回值 const fun = (fn: () => void) => {};

declare 作用?

declare关键字在 TypeScript 中主要用于声明类型信息,而不是实际的实现代码。其帮助 TypeScript 理解代码中的类型结构,从而提供更好的类型检查和代码提示等功能。

声明全局变量类型

当在项目中使用了某些全局变量,而这些变量没有明确的类型定义时,可以使用declare来声明它们的类型。

ts 复制代码
declare const globalVar: string;

如,在 UmiJS 框架 中,针对全局变量声明类型:

ts 复制代码
// typings.d.ts
declare function log(info: string): void; 
declare global {
  type AnalysisFunction = (params: { pageId: string; eventId: string; ext?: object }) => void;
	interface Window: {
    // 基座挂载window判断权限方法
   	PermissionAuth?: (params: {
      // 页面唯一标识
      pk: string;
      // 按钮唯一标识
      fk?: string;
    }) => boolean;
  }
}

声明模块

如果要使用一个外部模块,但这个模块没有提供 TypeScript 的类型定义文件(.d.ts),可以通过declare module来声明它的类型。

ts 复制代码
declare module 'my-custom-module' {
    export function doSomething(): void;
}

.d.ts.ts 区别?

  • .d.ts:第三方库提供类型声明、声明全局变量或全局类型、扩展现有模块的类型声明

    ts 复制代码
    // jquery.d.ts
    declare var jQuery: {
        (selector: string): any;
        ajax: {
            (url: string, settings?: any): void;
        };
    };
  • .ts:定义实际的类型和接口,并且这些类型和接口将被项目中的其他代码使用时,或者在项目内部进行模块化开发时

    ts 复制代码
    // user.ts
    export interface User {
        id: number;
        name: string;
        email: string;
    }

import typeimport 区别?

  • import type:用于导入类型信息,但不会将导入的模块包含在运行时代码中。它主要用于类型声明,不会影响最终的 JavaScript 输出

    ts 复制代码
    // main.ts
    import type { User } from './types';
  • import:用于导入模块中的值(如变量、函数、类等),这些值在运行时会被加载和执行。它不仅用于类型声明,还用于实际的代码运行

extends 扩展类型

interface 上的 extends 关键字允许从其他命名类型复制成员,并添加任何新成员。

这对于减少类型声明的数量以及表明同一属性的几个不同的意图很有用。

ts 复制代码
interface Person {
  name: string;
  age: number;
}
// 类型扩展
interface Man extends Person {
  readonly sex: '男';
}

interface SpecialSkill {
  fly: () => void;
}
// 也可以从多种类型扩展
interface Superman extends Man, SpecialSkill {}

typeofkeyof 区别?

  • typeof 操作符用于获取一个变量或属性的类型

  • keyof 操作符用于获取一个对象类型的所有键的联合类型

ts 复制代码
interface IPerson {
  name: string;
  age: number;
}

type PersonKey = keyof IPerson; // 类型是 "name" | "age"
const p: PersonKey = 'age';
const pType = typeof p; // string

更多内容,可详见:TypeScript系列:第四篇 - typeof 与 keyof

extends keyof 类型约束

在动态访问和操作对象属性时保持类型安全,避免运行时错误。

<TData, TLabelKey extends keyof TData> 定义两个泛型参数TDataTLabelKey

  • TData表示传入的对象类型
  • TLabelKey表示对象中的某个键,该键必须是TData对象的一个键(通过extends keyof TData约束)
动态访问对象属性

动态地访问对象的某个属性时,可以使用这种约束来确保访问的属性是有效的。

ts 复制代码
interface TData {
    city: string;
    adcode: number;
}

// labelKey必须是data对象的键之一
function getValue<TData, TLabelKey extends keyof TData>(data: TData, labelKey: TLabelKey): TData[TLabelKey] {
    return data[labelKey];
}
动态生成对象属性
ts 复制代码
interface TData {
    city: string;
    adcode: number;
}

// labelKey必须是data对象的键之一,value的类型必须与data[labelKey]的类型一致
function setProperty<TData, TLabelKey extends keyof TData>(data: TData, labelKey: TLabelKey, value: TData[TLabelKey]): TData {
    data[labelKey] = value;
    return data;
}

!非空断言

在不进行任何显式检查的情况下从类型中删除 nullundefined

ts 复制代码
function liveDangerously(x?: number) {
  return x.toFixed(); 	// x可能为"未定义"
  return x!.toFixed();  // ✔️
}
  • 绕过类型检查 :当确定一个变量不会是nullundefined,但 TypeScript 编译器无法确定时,使用 !来绕过类型检查
  • 避免编译错误 :在某些情况下,TypeScript会报错,因为其认为一个变量可能是nullundefined。使用!可以避免编译错误

模板字符串类型的排列组合

获得一组规律固定,可由排列组合得到的联合类型

ts 复制代码
type A = 'a1' | 'a2';
type B = 'b1' | 'b2';

type Products = `${A}-${B}`; // "a1-b1" | "a1-b2" | "a2-b1" | "a2-b2"

实用工具类型

ts 复制代码
interface A {
  a: number;
  b: number;
  c: number;
}
interface B {
  c: number;
  d: number;
}
方法 作用 示例 结果
Partial<Type> 构造一个将 Type 的所有属性设置为可选的类型 Partial<A & B> {a?: number, b?: number, c?: number}
Pick<Type, Keys> Type 中选取一组属性 Keys来构造一个类型 `Pick<A, 'a' 'c'>`
Omit<Type, Keys> Type 中选择所有属性然后删除 Keys来构造一个类型 `Omit<A, 'a' 'c'>`
Extract<Type, Union> Type 中提取所有可分配给 Union 的联合成员来构造一个类型 `Extract<A B, B>`
相关推荐
翻滚吧键盘27 分钟前
js代码09
开发语言·javascript·ecmascript
Amy.Wang1 小时前
前端如何实现电子签名
前端·javascript·html5
海天胜景1 小时前
vue3 el-table 行筛选 设置为单选
javascript·vue.js·elementui
今天又在摸鱼1 小时前
Vue3-组件化-Vue核心思想之一
前端·javascript·vue.js
百锦再1 小时前
Vue中对象赋值问题:对象引用被保留,仅部分属性被覆盖
前端·javascript·vue.js·vue·web·reactive·ref
jingling5552 小时前
面试版-前端开发核心知识
开发语言·前端·javascript·vue.js·面试·前端框架
FogLetter2 小时前
图片懒加载:让网页飞起来的魔法技巧 ✨
前端·javascript·css
拾光拾趣录2 小时前
JavaScript 加载对浏览器渲染的影响
前端·javascript·浏览器
浪裡遊4 小时前
Sass详解:功能特性、常用方法与最佳实践
开发语言·前端·javascript·css·vue.js·rust·sass
夏梦春蝉5 小时前
ES6从入门到精通:常用知识点
前端·javascript·es6