十个你必须要会的TypeScript技巧

news2024/11/20 8:30:00

大厂面试题分享 面试题库

前后端面试题库 (面试必备) 推荐:★★★★★

地址:前端面试题库  web前端面试题库 VS java后端面试题库大全


1. 泛型的使用

泛型可以让我们编写更具灵活性、可重用性和类型安全性的代码。在 TypeScript 中,泛型通常使用类型参数来定义一个通用的类型或函数,并在使用时指定具体的类型。

我们想编写一个函数来反转任意数组,假设我们不使用泛型,代码可能是这样

function reverseStrings(items: string[]): string[] {
  return items.reverse();
}

function reverseNumbers(items: number[]): number[] {
  return items.reverse();
}

但是这种方法显然不够优雅,因为我们需要分别编写两个函数来处理 string 和 number 类型的数组,并且当我们需要处理其他类型的数组时,我们必须再次编写新的函数。

使用泛型,我们可以很容易地创建一个通用的函数来处理任何类型的数组:

function reverse<T>(items: T[]): T[] {
  return items.reverse();
}

const words = ['hello', 'world'];
const reversedWords = reverse<string>(words); 
console.log(reversedWords); // ['world', 'hello']

const numbers = [1, 2, 3];
const reversedNumbers = reverse<number>(numbers);
console.log(reversedNumbers); // [3, 2, 1]

2. 利用交叉类型和联合类型

交叉类型(Intersection Types)允许将多个类型合并为一个类型,新类型将具有所有类型的特性。我们可以使用符号 & 运算符将两个或多个类型组合成一个交叉类型。

联合类型(Union Types)表示一个值可以有多种类型之一。我们可以使用符号 | 运算符将两个或多个类型组合成一个联合类型。

// 交叉类型
interface Dog {
  walk(): void;
}

interface Cat {
  meow(): void;
}

type Pet = Dog & Cat;

const myPet: Pet = {
  walk() { console.log('walking') },
  meow() { console.log('meowing') }
}

联合类型的使用也非常简单,用法就是使用 | 运算符将多个类型组合在一起

interface Square {
  side: number;
}

interface Circle {
  radius: number;
}

function calculateArea(shape: Square | Circle) {
  if ('side' in shape) {
    return shape.side ** 2;
  } else {
    return Math.PI * shape.radius ** 2;
  }
}

3. 类型推断

类型推断(Type Inference)是 TypeScript 的一个强大的特性。它允许编译器根据上下文自动推断出变量的类型,从而减少手动输入类型的工作量,同时也提高了代码的可维护性和可读性。

栗子

let num = 5;
let str = "hello";
let bool = true;

function add(a: number, b: number) {
  return a + b;
}

let result = add(num, 10);

4. keyof

keyof 是 TypeScript 中的一个关键字,用于获取对象类型的所有键的联合类型。它可以帮助我们在编写泛型函数或操作对象属性时,提供更好的类型安全性。

interface Person {
  name: string;
  age: number;
  gender: 'male' | 'female';
}

function getProperty<T, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

let person: Person = { name: 'Alice', age: 30, gender: 'female' };
let name = getProperty(person, 'name');
let age = getProperty(person, 'age');
let gender = getProperty(person, 'gender');

5. 映射类型

TypeScript 中的映射类型(Mapped Types)是一种非常强大的类型操作符,它可以根据一个已有的对象类型,生成一个新的对象类型。映射类型可以帮助我们进行一些常见的类型转换和操作,如将所有属性变成可选属性、添加或删除属性、修改属性类型等等。

TypeScript 中的映射类型有以下四种:

  1. Partial<T>:将类型 T 中所有属性变为可选属性。
interface Person {
  name: string;
  age: number;
  gender: 'male' | 'female';
}

type PartialPerson = Partial<Person>;

// 等价于
// interface PartialPerson {
//   name?: string;
//   age?: number;
//   gender?: 'male' | 'female';
// }
  1. Readonly<T>:将类型 T 中所有属性变为只读属性。
interface Person {
  name: string;
  age: number;
  gender: 'male' | 'female';
}

type ReadonlyPerson = Readonly<Person>;

// 等价于
// interface ReadonlyPerson {
//   readonly name: string;
//   readonly age: number;
//   readonly gender: 'male' | 'female';
// }
  1. Record<K, T>:创建一个新的对象类型,其属性名类型为 K,属性值类型为 T
type Dictionary<T> = Record<string, T>;

let dict: Dictionary<number> = {
  foo: 123,
  bar: 456,
};
  1. Pick<T, K>:从类型 T 中选择指定的属性 K,并返回一个新的对象类型。
interface Person {
  name: string;
  age: number;
  gender: 'male' | 'female';
}

type PersonNameAndAge = Pick<Person, 'name' | 'age'>;

// 等价于
// interface PersonNameAndAge {
//   name: string;
//   age: number;
// }

还有一种映射类型叫做 Keyof,它用于获取一个对象类型中所有属性名组成的联合类型。这个类型在前面的问题中已经讲到过了,这里就不再赘述。

6. 类型别名和接口

1. 类型别名

类型别名(Type Aliases)是一种给一个已经存在的类型起一个新的名字的方式。通过 type 关键字可以定义一个类型别名。

type MyString = string;
type MyNumber = number;

type Person = {
  name: string;
  age: number;
};

类型别名可以很方便地给复杂的类型定义一个简洁的名称,从而提高代码可读性,并且还可以使用联合类型、交叉类型等高级类型

type Color = 'red' | 'green' | 'blue';
type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; length: number };

function draw(shape: Shape, color: Color) {
  // ...
}

2.接口

接口(Interfaces)是一种描述对象结构的方式,在 TypeScript 中通过 interface 关键字来定义。接口可以包含属性、方法和索引签名等

interface Person {
  name: string;
  age: number;
  sayHello: () => void;
}

let person: Person = {
  name: 'Alice',
  age: 30,
  sayHello() {
    console.log(`Hello, my name is ${this.name}.`);
  },
};

接口在描述对象结构时非常有用,它可以提供更好的代码组织性和可读性,并且也可以在一些特定场景下提供更好的类型安全性。另外需要注意的是,接口只能描述对象的形状,不能描述具体的实现方式。如果需要描述具体的实现方式,可以使用类或函数类型。

7. 装饰器

装饰器是一种特殊的语法,它可以用来修饰类、方法、属性以及参数等元素,从而达到一些特定的目的。在 TypeScript 中,我们可以使用 @ 符号来声明一个装饰器

function log(target: any, key: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function(...args: any[]) {
    console.log(`Call ${key} with args: ${JSON.stringify(args)}`);
    return originalMethod.apply(this, args);
  };
  return descriptor;
}

class MyClass {
  @log
  greet(name: string) {
    console.log(`Hello, ${name}!`);
  }
}

TypeScript 中的装饰器可以用于很多场景,例如实现依赖注入、自动绑定事件、路由映射等等。常见的装饰器包括 @Injectable@Component@ViewChild@RouterConfig 等等。

8. 类型守卫

类型守卫(Type Guards)是 TypeScript 中用来检测类型的一种机制,它可以帮助开发者在运行时检测某个变量的类型,并在不同的条件下提供不同的类型声明。

在 TypeScript 中,有四种常见的类型守卫方式:

  1. typeof 类型守卫
function foo(x: number | string) {
  if (typeof x === 'number') {
    // x is number
  } else {
    // x is string
  }
}
  1. instanceof 类型守卫
class MyClass {}

function foo(x: any) {
  if (x instanceof MyClass) {
    // x is an instance of MyClass
  }
}
  1. 自定义类型守卫函数
interface A { a: number }
interface B { b: number }

function isA(x: any): x is A {
  return typeof x.a === 'number';
}

function foo(x: A | B) {
  if (isA(x)) {
    // x is an instance of A
  } else {
    // x is an instance of B
  }
}
  1. in 操作符类型守卫
interface A { a: number }
interface B { b: number }

function foo(x: A | B) {
  if ('a' in x) {
    // x is an instance of A
  } else {
    // x is an instance of B
  }
}

9. 声明文件

声明文件(Declaration File)是一种特殊的类型文件,用来描述外部 JavaScript 库、模块或对象的类型,以便在 TypeScript 代码中正确引用和使用它们。

TypeScript 编译器可以根据 JavaScript 库的源代码推断出其类型信息,但某些 JavaScript 库并没有提供类型定义文件,或者类型定义文件不完整或不准确,这时我们需要手动编写声明文件。声明文件的扩展名为 .d.ts,可以与 TypeScript 文件一起放置在项目目录中。声明文件的编写方式有以下几种:

  1. 定义全局变量和函数

如果我们需要在 TypeScript 代码中调用浏览器原生 API 或其他 JavaScript 库中的全局变量和函数,就需要手动编写声明文件来告诉 TypeScript 对应变量和函数的类型。例如:

declare const $: (selector: string) => any;

$('#my-element').addClass('highlight');
  1. 扩展已有类型

有时候我们需要扩展已有的类型定义,以适应自己的需求,这时可以使用 interfacenamespace 等关键字来定义和扩展类型。例如:

interface String {
  reverse(): string;
}

const str = 'Hello, world!';
console.log(str.reverse()); // "!dlrow ,olleH"
  1. 模块声明

如果我们要使用一个已有的 JavaScript 模块,但模块本身没有提供类型定义文件,或者类型定义文件不完整或不准确,这时我们需要手动编写声明文件来告诉 TypeScript 模块的类型信息。例如:

declare module 'my-module' {
  export function greet(name: string): string;
}

10. 类型化事件

类型化事件(Typed Event)是一种可以指定事件处理函数接收参数类型、返回值类型的事件机制。通过使用类型化事件,我们可以在编译时对事件处理函数的类型进行检查,以避免运行时因类型不匹配而导致的错误。

举个栗子,如何定义和使用类型化事件

interface EventHandler<T> {
  (args: T): void;
}

class TypedEvent<T> {
  private handlers: EventHandler<T>[] = [];

  public addHandler(handler: EventHandler<T>) {
    this.handlers.push(handler);
  }

  public removeHandler(handler: EventHandler<T>) {
    const index = this.handlers.indexOf(handler);
    if (index >= 0) {
      this.handlers.splice(index, 1);
    }
  }

  public raise(args: T) {
    for (const handler of this.handlers) {
      handler(args);
    }
  }
}

// 定义一个事件参数类型
interface MyEventArgs {
  message: string;
}

// 创建一个类型化事件实例
const myEvent = new TypedEvent<MyEventArgs>();

// 添加一个事件处理函数
myEvent.addHandler((args: MyEventArgs) => {
  console.log(args.message);
});

// 触发事件
myEvent.raise({ message: 'Hello, world!' });

 大厂面试题分享 面试题库

前后端面试题库 (面试必备) 推荐:★★★★★

地址:前端面试题库  web前端面试题库 VS java后端面试题库大全

 

广州 0月租卡是什么?0月租能注册各大平台账号吗? 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/669550.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot中15个常用启动扩展点,你用过几个?

背景 Spring的核心思想就是容器&#xff0c;当容器refresh的时候&#xff0c;外部看上去风平浪静&#xff0c;其实内部则是一片惊涛骇浪&#xff0c;汪洋一片。Springboot更是封装了Spring&#xff0c;遵循约定大于配置&#xff0c;加上自动装配的机制。很多时候我们只要引用了…

P35[10-5]硬件IIC配置+读写MPU6050(软)(此处注意与软件iic区别)

接线图如下: 注:硬件读写iic的连接位置固定,可参考引脚定义表(如下) 声明:I2C1重映射时,有一次更换机会,但是此面包板由于OLED的该引脚无法接线,因此只能接在PB10 PB11的I2C2上 软件iic初始化部分:(此处即可替代掉整个软件iic.c初始化的底层) void MPU6050_Init(vo…

知识变现绝学,3个步骤5个技巧用你的知识盈利?

知识就是生产力。 有的人通过学习各类知识&#xff0c;结果变的更加迷茫&#xff0c;更加没有方向了。 而有的人通过学习各类知识&#xff0c;结果变成了专家&#xff0c;实现了财务自由。 你想知道如何把这生产力去变现呢&#xff1f; 这个时代信息泛滥&#xff0c;人才泛滥…

305 · 矩阵中的最长递增路径

链接&#xff1a;LintCode 炼码 题解&#xff1a;九章算法 - 帮助更多程序员找到好工作&#xff0c;硅谷顶尖IT企业工程师实时在线授课为你传授面试技巧 class Solution { public:/*** param matrix: A matrix* return: An integer.*/class Node {public:int x;int y;int val…

DNDC模型四:土壤碳储量与作物产量、农田减排潜力分析

查看原文>>>双碳目标下DNDC模型建模方法及在土壤碳储量、温室气体排放、农田减排、土地变化、气候变化中的实践应用 由于全球变暖、大气中温室气体浓度逐年增加等问题的出现&#xff0c;“双碳”行动特别是碳中和已经在世界范围形成广泛影响。国家领导人在多次重要会…

遥感影像处理-监督分类

遥感图像分类是图像信息提取的一种方法&#xff0c;是遥感数字图像处理的重要环节&#xff0c;也是遥感应用最广泛的领域之一&#xff0c;其中提取土地利用分类信息也是常见的应用领域。本推文简要介绍了图像分类的原理和方式&#xff0c;并着重介绍了最大似然分类法监督分类在…

分布式事务:XA和Seata的XA模式

大家好&#xff0c;我是方圆。上一篇博客《从2PC和容错共识算法讨论zookeeper中的Create请求》介绍了保证分布式事务提交的两阶段提交协议&#xff0c;而XA是针对两阶段提交提出的接口实现标准&#xff0c;本文则对XA进行介绍。 1. XA XA &#xff08;eXtended Architecture …

第一章 基础算法(三)—— 双指针,位运算,离散化与区间合并

文章目录 双指针位运算离散化区间合并双指针练习题799. 最长连续不重复子序列800. 数组元素的目标和2816. 判断子序列 位运算练习题801. 二进制中1的个数 离散化练习题802. 区间和 区间合并练习题803. 区间合并 为什么直接用y总的板书&#xff1f; 我是懒狗&#xff0c;不想再画…

音乐考级系统python+mysql

目录 废话不多说下面看严谨版不带web界面的&#xff1a; 总结&#xff1a; 写这个博客呢主要是因为之前学校有个简单的课设要做&#xff0c;想着白嫖一个交差的&#xff0c;但是找了一圈没找到合适的能拿来用的&#xff0c;我就下班用了两晚手搓了一个代码。 具体的建表语句…

PCB设计实验|第二周|谐波振荡电路实验|3月6日

目录 实验二 谐波振荡电路实验 一、实验原理 二、实验环境 三、实验结果及分析 四、实验总结 实验二 谐波振荡电路实验 一、实验原理 利用深度正反馈&#xff0c;通过阻容耦合使两个电子器件交替导通与截止&#xff0c;从而自激产生方波输出的振荡器&#xff0c;常用作…

ChatGPT在前,华为盘古Chat在后

国产盘古Chat对话方面堪比GPT-3.5 什么是ChatGPT&#xff1f;简单来说&#xff0c;就是一个能够和人类自然对话的人工智能系统。它可以理解你的语言&#xff0c;回答你的问题&#xff0c;甚至给你提供建议和服务。它不仅可以处理文字&#xff0c;还可以处理图片、视频、音频等…

基于Java学生信息管理系统-控制台版

基于Java学生信息管理系统-控制台版 一、系统介绍二、功能展示1.学生信息添加2.学生信息修改3.学生信息查询4.学生信息删除5.退出系统 三、代码展示四、其它1.其他系统实现2.获取源码 一、系统介绍 学生信息的添加、修改、删除、查询、退出系统 二、功能展示 1.学生信息添加…

【面试题01】抽象类、接口 的区别和使用场景

文章目录 一、抽象类和接口的区别1.1 定义方式不同1.2.成员方法不同1.3 实现方式不同1.4 构造方法不同1.5 访问修饰符不同1.6 关注点不同 二、抽象类和接口的使用场景2.1 抽象类的使用场景2.2 接口的使用场景 三、PHP代码演示总结 一、抽象类和接口的区别 抽象类和接口基本上是…

提升效率,使用ChatGPT的轻松撰写日报和周报

日报和周报是办公生活中不可或缺的部分&#xff0c;它们有助于记录工作进展、分享关键信息和与团队保持沟通。但是&#xff0c;有时写作这些报告可能会变得繁琐和耗时。在本文中&#xff0c;我们将介绍如何利用ChatGPT&#xff0c;一个强大的自然语言处理模型&#xff0c;提高写…

安卓开发级联显示菜单-省市区显示举例

安卓开发级联显示菜单-省市区显示举例 问题背景 安卓日常开发过程&#xff0c;经常会有需要级联显示的场景&#xff0c;比如省市区显示等&#xff0c;或者各种组织结构级联显示&#xff0c;本文将介绍安卓开发过程实现级联显示的一种方案。 实现效果如下&#xff1a; 问题分…

GaussDB整体性能慢分析

目录 问题描述问题现象告警业务影响原因分析分析步骤分析定位方法步骤一步骤二步骤三步骤四CPU满I/O满或者I/O异常内存满网络异常 步骤五并发问题数据库配置问题异常等待事件长时间性能下降短时性能抖动不优SQL 问题描述 整体性能慢。不满足客户作业对时延要求或者不满足客户预…

微信小程序 u-picker 三级联动 uView

微信小程序 u-picker 三级联动 uView 场景 移动端微信小程序框架 uView 中的 u-picker 实现三级联动 数据是一级一级加载的 [12,1201,120101] 多列联动 先了解属性参数 mode可以设置为&#xff1a;time、region、selector、multiSelector&#xff0c;区分时间、地区、单列&am…

2022高教社杯全国大学生数学建模竞赛B题解析(更新完结)

2022高教社杯全国大学生数学建模竞赛B题解析&#xff08;更新完结&#xff09; 题目解析前言问题一1.11.21.3问题二 题目 B 题 无人机遂行编队飞行中的纯方位无源定位 无人机集群在遂行编队飞行时&#xff0c;为避免外界干扰&#xff0c;应尽可能保持电磁静默&#xff0c;少向…

LC-LCP 41. 黑白翻转棋

LCP 41. 黑白翻转棋 难度中等32 在 n*m 大小的棋盘中&#xff0c;有黑白两种棋子&#xff0c;黑棋记作字母 "X", 白棋记作字母 "O"&#xff0c;空余位置记作 "."。当落下的棋子与其他相同颜色的棋子在行、列或对角线完全包围&#xff08;中间不…

Kotlin 一劳永逸实现 TAG

1 TAG 经典写法 对于 Android 开发&#xff0c;当我们需要在类中打印 Log 时&#xff0c;通常在Java中会这么定义一个 TAG&#xff1a; private static final String TAG "TestClass"; 或者不具体指定名字&#xff1a; private static final String TAG TestClass.…