【利用AI刷面试题】AI:十道不常见的TypeScript面试题

news2024/11/27 14:28:43

在这里插入图片描述

文章目录

  • 前言😏
  • 以下是关于 TypeScript 的一些偏僻的面试题😝
    • 1. 泛型约束中的 "extends" 关键字有哪些用法和含义?
    • 2. 什么是交叉类型(Intersection Types)?如何在 TypeScript 中定义和使用它们?
    • 3. 请解释 "keyof" 关键字的含义和用法。
    • 4. TypeScript 中的 "never" 类型有什么用途?请给出一个示例。
    • 5. 如何定义一个函数类型的 "this" 参数,以及在函数中使用它?
    • 6. 如果有一个类型是由两个类型 "A" 和 "B" 组成的,并且 "A" 和 "B" 中都有相同的属性名,那么 TypeScript 会怎么处理?
    • 7. 如果有一个变量的类型是 "unknown",那么我们在使用这个变量时需要注意什么?
    • 8. TypeScript 中有哪些用于描述`函数形参`、`返回值`和`异常的装饰器`?
    • 9. `TypeScript` 中在类型声明中使用 "`import`" 语句,这有什么作用?
    • 10. 如果两个模块的接口名称相同,但结构不同,那么如何在 TypeScript 中合并这两个接口?

前言😏

笔者曾经被问道:TypeScript 中 type 和 interface 区别

我们看看AI的答案:

在 TypeScript 中,typeinterface 都可以用来定义类型。它们的主要区别在于:type 可以定义任何类型,而 interface 只能定义对象类型。

具体来说,type 可以定义原始类型、联合类型、元组类型和函数类型等,而 interface 只能定义对象类型、类类型和函数类型。

下面是一些具体的区别:

  1. type 可以定义原始类型、联合类型、元组类型和函数类型,而 interface 只能定义对象类型、类类型和函数类型
type MyType = boolean | number; // 原始类型和联合类型
interface MyInterface {
  x: number;
  y: number;
} // 对象类型

type Point = [number, number]; // 元组类型
interface Shape {
  color: string;
  width: number;
  height: number;
} // 对象类型

type HelloFunction = (name: string) => string; // 函数类型
interface GoodbyeFunction {
  (name: string): string;
} // 函数类型
  1. type 支持 extends 和交叉类型,而 interface 只支持 extends。
type Base = { x: number };
type Derived = Base & { y: number };
type Merged = Base | { z: number };

interface Base {
  x: number;
}
interface Derived extends Base {
  y: number;
}
  1. interface 支持定义合并,即多个同名接口的成员会合并为同一个接口,而 type 不支持。
interface Foo {
  x: number;
}
interface Foo {
  y: string;
}

const obj: Foo = { x: 1, y: "hello" };

总之,typeinterface 都是用于定义类型的关键字,它们的主要区别在于 type 可以定义任何类型,而 interface 只能定义对象类型。

在选择使用哪种关键字时,应根据需要来确定使用范围和功能,以提高代码的可读性和可维护性。

😊以上就是AI的回答啦~

那么,对于没有深入研究且在实际开发中没有使用到 TypeScript 的我,在回答那个问题的时候自然是回答不上的,现在有了AI的加持,可以多学习了,不仅问了下AI上面那个问题的答案,并且让AI列举了十道关于TypeScript那些偏僻的面试题

以下是关于 TypeScript 的一些偏僻的面试题😝

1. 泛型约束中的 “extends” 关键字有哪些用法和含义?

在 TypeScript 中,“extends” 关键字在泛型约束中使用,用来指定泛型类型的类型范围和约束条件,可以用于:

1. 类型的范围约束

在泛型定义中,使用 “extends” 关键字,指定一个类型约束范围,确保泛型所接收的类型必须是一个指定的类型。

例如:

interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
}

loggingIdentity('123'); // 输出 3

上面的例子中,“T extends Lengthwise” 说明泛型变量 “T” 必须满足 “Lengthwise” 接口的约束(即具有 length 属性)。

2. 多重约束

可以针对一个泛型变量指定多个约束条件。

例如:

function copyFields<T extends U, U>(target: T, source: U): T {
    for (let id in source) {
        target[id] = (<T>source)[id];
    }
    return target;
}

let x = { a:1, b: 2, c: 3, d: 4 };
copyFields(x, { b: 10, d: 20 }); //这个是可以的.
copyFields(x, { e: 10 }); // error,因为 source 上不存在 a 或 b 属性

上面的例子中,“T extends U” 说明泛型变量 “T” 必须满足 “U” 类型的约束,从而确定了泛型变量 “T” 的类型范围。

总之,“extends” 关键字用于定义泛型类型的约束条件,可以指定类型或接口,也可以定义多重约束。在使用泛型的时候,可以通过对泛型变量进行约束,确保所传入的参数符合预期类型。

2. 什么是交叉类型(Intersection Types)?如何在 TypeScript 中定义和使用它们?

交叉类型(Intersection Types)是 TypeScript 中表示多个类型的交集的一种方式。

简单地说,它可以将多个类型合并成一个类型。例如,假设有两个类型 A 和 B:

interface A {
  propA: string;
}

interface B {
  propB: number;
}

我们可以使用交叉类型将这两个类型合并成一个类型,定义一个新的类型 C:

type C = A & B;

交叉类型 C 包含了类型 AB 中的所有属性。因此,变量 c 可以包含 propApropB 两个属性:

const c: C = { propA: "hello", propB: 123 };

交叉类型也可以使用于继承:

class D implements A, B {
  propA = "hello";
  propB = 123;
}

在使用交叉类型时,需要注意以下几点:

  1. 只有需要合并类型的属性之间类型冲突时才需要使用交叉类型,否则使用普通类型即可。
  2. 交叉类型中所有类型的属性都必须满足相同的访问限制(public、private 或 protected)。
  3. Union 类型和 Intersection 类型的区别在于,Union 类型表示多个类型中的任意一个,而交叉类型表示多个类型的交集。

3. 请解释 “keyof” 关键字的含义和用法。

“keyof” 关键字是 TypeScript 中用来获取一个类型(通常是对象类型)所有键(即属性名)的名称的一种方式。

举个例子,假设有一个对象类型:

type Person = {
  name: string;
  age: number;
  gender: "male" | "female";
};

我们可以使用 “keyof” 关键字获取这个对象类型的所有键的名称:

type PersonKeys = keyof Person;
// 等价于 type PersonKeys = "name" | "age" | "gender"

这意味着我们可以使用 PersonKeys 来定义一个函数的参数类型:

function getProp(obj: Person, key: keyof Person): any {
  return obj[key];
}

在上面的例子中,参数 key 的类型指定为 “keyof Person”,即表示参数 key 必须为 Person 中定义过的属性名,而不是其他任何字符串。

此外,“keyof” 关键字还可以用于泛型类型或条件类型:

type PersonPropTypes<T> = {
  [K in keyof T]: T[K] extends string ? K : never;
};

在上面的例子中,我们使用了泛型 “T” 和 “keyof T”。“keyof T” 表示泛型 T 中所有属性名的联合类型,我们可以通过它来定义一个新的对象类型,该对象类型只包含泛型 T 中值为字符串类型的属性名。

总之,“keyof” 关键字是一种非常有用的 TypeScript 特性,它常用于为对象类型中的属性命名并且在编写通用代码时提供了更好的类型检查。

4. TypeScript 中的 “never” 类型有什么用途?请给出一个示例。

在 TypeScript 中,never 类型表示一个永远不会发生的值的类型。在实际应用中,never 类型常常用在联合类型或泛型中:

1. 当函数的返回类型为 never 时,表示该函数永远不会正常返回。

function throwError(message: string): never {
  throw new Error(message);
}

2. 在泛型中使用 never 类型可以帮助我们确保某些类型不会被传递进来,进而让编译器帮我们避免一些可能出现的错误。

type NonNullable<T> = T extends null | undefined ? never : T;

type MyRequired<T> = {
  [P in keyof T]-?: NonNullable<T[P]>;
};

上面的代码定义了一个泛型 NonNullable 和一个接口 MyRequired。其中,NonNullable 可以将 nullundefined 类型转化为 never 类型,而 MyRequired 可以将每个属性变为必需的,并且确保不会出现 nullundefined

总的来说,never 类型的主要用途是辅助开发人员进行类型检查和编写更加安全的代码。

5. 如何定义一个函数类型的 “this” 参数,以及在函数中使用它?

TypeScript 中可以使用 this 参数来显式地指定函数内部的 this 指向。在函数类型上,我们可以使用 this 参数来描述该函数期望的上下文对象类型。

具体的用法如下:

// 定义一个接口来描述需要使用 this 参数的函数类型
interface MyFunc {
  (this: Window, event: Event): void;
}

// 定义一个对象,在该对象上调用函数时需要使用 this 参数来指定上下文对象
const myObj = {
  handleClick: function(this: Window, event: Event) {
    console.log("event: ", event);
    console.log("this: ", this);
  }
};

// 在对象上调用函数时需要使用 call 或 apply 方法来指定上下文对象
myObj.handleClick.call(window, new Event("click"));

上面的代码定义了一个函数类型 MyFunc,它期望 this 指向 Window 对象。同时,我们也定义了一个对象 myObj,并在其上面定义了一个函数 handleClick,需要使用 this 参数来指定上下文对象。最后,在调用 myObj.handleClick 时,我们使用 call 方法来显式地指定 this 指向 window 对象。

需要注意的是,使用 this 参数需要显式地指定上下文对象的类型,这样可以在编译期间发现一些潜在的问题,让代码更加健壮。

6. 如果有一个类型是由两个类型 “A” 和 “B” 组成的,并且 “A” 和 “B” 中都有相同的属性名,那么 TypeScript 会怎么处理?

如果一个类型是由两个类型 “A” 和 “B” 组成的,并且 “A” 和 “B” 中都有相同的属性名,则 TypeScript 会合并这两个类型,并且将属性的类型合并为联合类型。

具体来说,TypeScript 会遵循以下规则:

  1. 对于非函数成员,如果两个类型中成员的类型相同,则最终类型中该成员的类型为这个相同的类型;如果两个类型中成员的类型不同,则最终类型中该成员的类型为两个成员的类型的联合类型。

  2. 对于函数成员,如果两个类型中成员的类型都为函数,则最终类型中该成员的类型为函数的重载列表。如果两个类型中函数成员的重载列表性质不同,则会出现编译时错误。

下面是一个示例,它展示了当类型 “A” 和类型 “B” 中都有一个属性 “name” 时,TypeScript 是如何合并它们的:

type A = {
  id: number;
  name: string | null;
};

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

type C = A & B;

const c: C = {
  id: 1,
  name: "Tom",
  age: 18
};

在上面的代码中,我们定义了两个类型 “A” 和 “B”,它们都有一个属性 “name”。然后,我们定义了一个类型 “C”,使用交叉类型将类型 “A” 和 “B” 合并在一起。最后,我们声明了一个变量 c,它的类型为 “C”,并且给它赋值了一个包含 “id”、“name” 和 “age” 属性的对象。

在上面的代码中,属性 “name” 的类型为 string | nullstring,TypeScript 会将它们的类型合并为 string | null 类型。因此,类型 “C” 中属性 “name” 的类型为 string | null

总之,当一个类型由两个类型合并而成时,TypeScript 遵循一定的规则来进行合并,以便编译器能够正确地推断类型,从而提高代码的可靠性。

7. 如果有一个变量的类型是 “unknown”,那么我们在使用这个变量时需要注意什么?

8. TypeScript 中有哪些用于描述函数形参返回值异常的装饰器

如果一个变量的类型是 unknown,那么在使用这个变量时需要注意它的类型是未知的,因此我们需要进行类型检查或类型断言来避免类型错误。

具体来说,我们在使用 unknown 类型的变量时,需要遵循以下规则:

  1. unknown 类型变量不能直接赋值给其他类型的变量,除了 any 类型和 unknown 类型本身之外。这是因为 any 类型可以隐式转换为任何类型,而 unknown 类型的变量可以接受任何类型的值,但我们必须显式地对其进行类型检查或类型断言才能使用它。

  2. 在对 unknown 类型的变量进行类型检查时,我们需要使用类型保护来确定其实际类型、属性和方法的可用性,以免在运行时出现类型错误。

下面是一个示例,它展示了如何使用类型检查或类型断言来使用 unknown 类型的变量:

function printLength(str: unknown) {
  if (typeof str === "string") {
    console.log(str.length); // OK
  } else {
    console.log("The type of str is unknown.");
  }
}

function getValue(obj: { [key: string]: unknown }, key: string) {
  if (typeof obj[key] === "string") {
    console.log(obj[key].toUpperCase()); // OK
  } else {
    console.log("The type of obj[key] is unknown.");
  }
}

let x: unknown = "hello";
let y: string;
let z: number;

// y = x; // 报错,无法将类型“unknown”分配给类型“string”
y = typeof x === "string" ? x : ""; // OK,使用类型断言
// z = x; // 报错,无法将类型“unknown”分配给类型“number”
z = typeof x === "number" ? x : 0; // OK,使用类型断言

在上面的代码中,我们定义了一个函数 printLength,它接受一个 unknown 类型的参数,并在函数内部使用类型检查来确定参数的实际类型,从而避免类型错误。另外,我们还定义了一个函数 getValue,它接受一个对象和一个键名,用于获取该键对应的值,并在函数内部对该值进行类型检查。最后,我们声明了一个 unknown 类型的变量 x,并使用类型检查和类型断言来使用该变量。

总之,在使用 unknown 类型变量时,我们需要特别注意其类型是未知的,需要进行类型检查或类型断言才能使用,以避免类型错误。

9. TypeScript 中在类型声明中使用 “import” 语句,这有什么作用?

在 TypeScript 中,有许多用于描述函数形参、返回值和异常的装饰器,具体如下:

1. @Paramtypes(...types: Function[]):用于描述函数的形参类型。

function MyFunc(@Paramtypes(Number, String) arg1: any) {
  // ...
}

2. @ReturnType(type: Function):用于描述函数的返回值类型。

@ReturnType(Number)
function MyFunc() {
  // ...
}

3. @Throws(...types: any[]):用于描述函数可能抛出的异常类型。

function MyFunc() {
  @Throws(Error)
  function innerFunc() {
    // ...
  }
  // ...
}

4. @Asserts(expression: string):用于描述函数的运行结果是否满足某个条件。

function MyFunc() {
  @Asserts("result.length > 0")
  function innerFunc(): string[] {
    const result = ["a", "b", "c"];
    return result;
  }
  // ...
}

这些装饰器都是 TypeScript 中的实验性特性,需要在 tsconfig.json 文件中启用 “experimentalDecorators” 选项才能使用。

使用这些装饰器可以帮助我们更好地描述函数的类型信息,从而提高代码的可靠性和维护性。

10. 如果两个模块的接口名称相同,但结构不同,那么如何在 TypeScript 中合并这两个接口?

在 TypeScript 中,可以使用模块合并来合并两个模块中的接口,具体的方法如下:

假设我们有两个模块 Module1Module2,它们中都定义了名称为 SampleInterface 的接口,但是它们的结构不同。我们可以在同一文件中定义一个新的模块 ModuleCombined,并将模块 Module1Module2 合并到这个模块中,然后在模块 ModuleCombined 中定义一个同名的接口,这个接口的结构就是将模块 Module1Module2 中的同名接口的成员合并起来的结果。

下面是示例代码:

// module1.ts
export interface SampleInterface {
  id: number;
  name: string;
}

// module2.ts
export interface SampleInterface {
  age: number;
  address: string;
}

// moduleCombined.ts
import { SampleInterface as Interface1 } from './module1';
import { SampleInterface as Interface2 } from './module2';

interface SampleInterface extends Interface1, Interface2 {}

export { SampleInterface };

在上面的代码中,我们分别定义了两个模块 Module1Module2,它们中都定义了名称为 SampleInterface 的接口,但接口的结构不同。然后我们创建了一个新的模块 ModuleCombined,并将模块 Module1Module2 合并到这个模块中,最后在模块 ModuleCombined 中定义了同名的接口 SampleInterface,这个接口继承了模块 Module1Module2 中的同名接口,从而将两个接口中的成员合并起来。

这样,我们就成功地合并了模块 Module1Module2 中的接口,可以通过 ModuleCombined.SampleInterface 来使用这个接口。注意,我们通过 import { SampleInterface as Interface1 } from './module1';import { SampleInterface as Interface2 } from './module2'; 来避免名称冲突。

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

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

相关文章

chatgpt赋能Python-pycharm如何加库

Pycharm如何加库&#xff1a;A Complete Guide Pycharm是一款强大的Python集成开发环境(IDE)&#xff0c;它提供了许多有用的工具和功能来简化Python开发过程。尽管Pycharm已经内置了许多功能和工具&#xff0c;但我们在开发过程中仍然需要使用第三方库来提高效率。在本文中&a…

MySQL 常用/见函数

目录 日期函数 字符串函数 数学函数 其它函数 日期函数 获得时分秒&#xff1a; select current_time(); ---------------- | current_time() | ---------------- | 13 :51:21 | ---------------- 日期就是date 获得时间戳&#xff1a; select current_timestamp()…

chatgpt赋能Python-pycharm中添加库

PyCharm中添加库&#xff1a;让你的Python开发更加高效 Python是一种高级编程语言&#xff0c;因其易学易用的特性被广泛应用于Web开发、数据分析、人工智能等领域。PyCharm是Python开发的一种强大的IDE&#xff08;集成开发环境&#xff09;&#xff0c;它提供了各种有用的功…

【P24】JMeter 正则表达式用户参数(RegEx User Parameters)

文章目录 一、准备工作二、测试计划设计 一、准备工作 慕慕生鲜&#xff1a; http://111.231.103.117/#/login 进入网页后&#xff0c;登录&#xff0c;页面提供了账户和密码 搜索框输入“虾” 右键检查或按F12&#xff0c;打开调试工具&#xff0c;点击搜索 二、测试计划设…

Hantek 5000系列示波器原理图研究

Hantek 5000系列是Hantek&#xff08;汉泰&#xff09;2010年左右推出的入门型号示波器&#xff0c;最高采样率1GSa/s&#xff0c;带宽200M。2010年的时候&#xff0c;EEVblog论坛的大佬tinhead给出了该系列示波器的详细原理图&#xff08;下载&#xff09;&#xff0c;图的可读…

RapidVideOCR(视频硬字幕提取工具)新增exe版

引言 考虑到提取视频字幕的小伙伴大多不是程序员行当&#xff0c;为了降低使用门槛&#xff0c;特此推出界面版的RapidVideOCR Desktop. RapidVideOCR Desktop需要搭配VideoSubFinder使用。它们两个关系如下图所示&#xff1a; #mermaid-svg-md1FaKkTWKdZahtd {font-family:&q…

Git常用命令rebase

Git常用命令rebase 1、git常用命令rebase rebase 会把你当前分支的 commit 放到公共分支的最后面&#xff0c;所以叫变基&#xff0c;就好像你从公共分支又重新拉出来这个 分支一样。 例如如果你从 master 拉了个 feature 分支出来&#xff0c;然后你提交了几个 commit&…

VScode添加右键运行、并设置每次运行前都清屏即去除之前的输出

一、添加右键运行 下载安装运行插件即可 二、运行前清屏 在运行插件中设置 找到Code-runner: Clear Previous Output&#xff0c;把√打上即可

同样都是PoE交换机,标准PoE交换机、非标准PoE交换机和非PoE交换机三者到底有啥区别?

网络交换机是企业和组织中构建局域网、企业网络和数据中心网络的重要组成部分。其中最常见的类型之一是PoE交换机。PoE交换机是一种允许通过网络线路提供电源和数据传输的交换机&#xff0c;这种技术可以为设备提供电力&#xff0c;避免了需要附加电源的麻烦。 本文将介绍PoE交…

从零制作操作系统——环境搭建以及HelloWorld

从零制作操作系统——环境搭建以及HelloWorld 起因 最近在学习操作系统&#xff0c;尝试自己照着书搓一个出来。 环境搭建 基础环境 我们的操作系统在x86平台的Linux下进行编写和运行。编辑器用的VIM。 我的系统是Fedora 36&#xff0c;当然你也可以使用Ubuntu或者其他Li…

码云(Gitee)与Git配置

前提 本文配置的前提是已经申请好了码云(gitee)的账号和电脑上已经安装好了git 1.配置gitee的ssh公钥 在gitee的个人设置里面配置ssh公钥 就是将公钥复制到右侧的框中, 并点击确定即可。 1.1生成ssh公钥 右键鼠标打开git bash here 输入如下命令&#xff0c;邮箱就填自己…

栈与队列的对决:如何用栈实现队列?

本篇博客会讲解力扣“232. 用栈实现队列”的解题思路&#xff0c;这是题目链接。 先来审题&#xff1a; 以下是输出示例&#xff1a; 以下是提示和进阶&#xff1a; 栈是一种后进先出的数据结构&#xff0c;而队列是一种先进先出的数据结构&#xff0c;如何用栈实现队列呢&…

chatgpt赋能Python-pingouin_python

了解Pingouin Python对数据分析和统计学的优势 介绍Pingouin Python Pingouin Python是一个强大的Python包&#xff0c;它提供了在数据分析和统计学中所需的一些主要函数。使用Pingouin Python可以方便地进行t-检验、方差分析、相关性等常用的数据分析和统计学任务。 此外&a…

Vue组件复杂表格高级编辑功能

Vue 组件复杂表格高级编辑功能 文章目录 Vue 组件复杂表格高级编辑功能1. sync 父子组件数据同步更新2. 在 el-table 中开发高级编辑表格功能3. 参考文献 在vue中组件的定义是希望组件可以做单一的功能&#xff0c;做到高复用&#xff0c;低耦合&#xff0c;所以父子组件之间的…

【每日一题/简单模拟题】2446. 判断两个事件是否存在冲突

⭐️前面的话⭐️ 本篇文章介绍【2446. 判断两个事件是否存在冲突】题解&#xff0c;算法标签&#xff1a;【模拟】&#xff0c;【字符串】&#xff0c;展示语言c/java。 &#x1f4d2;博客主页&#xff1a;未见花闻的博客主页 &#x1f389;欢迎关注&#x1f50e;点赞&#x…

某大学信息安全竞赛——栈迁移加强版——只溢出0x8,无限ROP

芝士题目&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1uwFlcSg94MuC2tPi-HCb9w 提取码&#xff1a;joj6 感悟&#xff1a; 之前我只做过溢出超过0x10这样的栈迁移&#xff0c;思路就是找机会去泄露栈空间的地址然后把栈迁移到我们可以控制的栈空间&#xff0c;亦…

Linux_证书_Openssl工具详解

文章目录 OpenSSLopenssl实现对称加密openssl生成密钥对、非对称加密、数字签名根据CA颁布证书生成ca私钥和ca证书根据ca生成证书 小结 OpenSSL OpenSSL 是一个开源项目&#xff0c;其组成主要包括一下三个组件&#xff1a; openssl&#xff1a;多用途的命令行工具 libcrypt…

【滤波专题-第7篇】“类EMD”算法分解后要怎样使用(3)——EMD降噪方法及MATLAB代码实现

使用EMD分解&#xff08;以及其他“类EMD”分解方法&#xff0c;以下为了简便统称EMD&#xff09;做信号降噪&#xff0c;是EMD的一个比较重要的应用方向。EMD可以将复杂的信号分解为一系列的固有模态函数&#xff08;IMFs&#xff09;&#xff0c;每一个IMF都包含了信号的一部…

“源擎”攻破银行核心系统建设痛点

银行业作为操作密集、数据密集、风险密集的行业&#xff0c;在向云转型的过程中面临着诸多独特的挑战&#xff0c;如银行需要具备不间断的业务创新能力&#xff0c;而不被系统开发周期制约&#xff1b;单一系统的开发和升级方式&#xff0c;越来越难以满足日益综合化的业务创新…

chatgpt赋能Python-pycharm关联python

Pycharm关联Python的介绍 Pycharm是一种非常流行的Python集成开发环境&#xff0c;开发人员可以在其中编写、调试和运行Python代码。Pycharm具有许多有用的功能&#xff0c;这些功能可以大大提高代码的效率和质量。其中一个最重要的功能是Pycharm如何关联Python&#xff0c;这…