技术面试题 - 第二部分 - Typescript
介绍
你好,你好!! :D
希望你们一切都好!
本系列第二部分来啦!🤓
在本章中,我们将深入探讨我在面试中遇到的关于 ✨Typescript✨ 的问题。
我就不多说了,让我们直接进入正题吧!
## 问题
1. TypeScript 中的泛型是什么?它是什么<T>?
2.接口和类型之间有什么区别?
3. `__type__`、`__type__` 、` __type__` 和` __type__` 之间有什么区别?anynullunknownnever
问题 1:TypeScript 中的泛型是什么?它是什么<T>?
简而言之……
TypeScript 中的泛型允许我们创建可重用的函数、类和接口,它们可以处理多种类型,而无需指定特定类型。这有助于避免使用 `T`
any作为万能类型。该语法
<T>用于声明泛型类型,但您也可以使用<V>,<M>, 或任何其他占位符。
它是如何运作的?
让我们举个例子来分析一下。
假设我有一个函数,它接受一个参数并返回一个相同类型的元素。如果我使用特定类型来编写这个函数,它看起来会是这样:
function returnElement(element: string): string {
return element;
}
const stringData = returnElement("Hello world");
我知道它的类型stringData是“字符串”,因为我已经声明过了。
但如果我想返回不同类型的数据呢?
const numberData = returnElement(5);
我会收到一条错误消息,因为类型与声明的类型不符。
解决方案可能是创建一个新函数来返回数字类型。
function returnNumber(element: number): number {
return element;
}
这种方法可行,但可能会导致代码重复。
避免这种情况的一个常见错误是使用any`T` 而不是声明的类型,但这违背了类型安全的目的。
function returnElement2(element: any): any {
return element;
}
然而,使用 `typeof`any会导致我们失去 TypeScript 的类型安全和错误检测功能。此外,如果每次需要避免重复代码时都
使用`typeof`,代码的可维护性将会降低。any
这正是使用通用名药物的优势所在。
function returnGenericElement<T>(element: T): T {
return element;
}
该函数将接收一个特定类型的元素;该类型将替换泛型,并在整个运行时保持不变。
这种方法使我们能够在保持类型安全的同时消除重复代码。
const stringData2 = returnGenericElement("Hello world");
const numberData2 = returnGenericElement(5);
但如果我需要一个来自数组的特定函数呢?
我们可以将泛型声明为数组,并像这样编写:
function returnLength<T>(element: T[]): number {
return element.length;
}
然后,
const stringLength = returnLength(["Hello", "world"]);
声明的类型将被作为参数提供的类型替换。
我们也可以在类中使用泛型。
class Addition<U> {
add: (x: U, y: U) => U;
}
关于这段代码,我有三点要说明:
add是一个匿名箭头函数(我在第一章中讨论过)。- 通用名称可以命名为
<U>,,<T>或者<Banana>,如果您愿意的话,甚至可以命名为。 - 由于我们尚未指定类型,因此无法在类内部实现操作。所以,我们需要先通过声明泛型类型来实例化类,然后再实现函数。
它的样子是这样的:
const operation = new Addition<number>();
operation.add = (x, y) => x + y; => We implement the function here
console.log(operation.add(5, 6)); // 11
最后,在结束这个问题之前,还有一点需要补充。
请记住,泛型是 TypeScript 的一个特性。这意味着当我们将其编译成 JavaScript 时,泛型会被清除。
从
function returnGenericElement<T>(element: T): T {
return element;
}
到
function returnGenericElement(element) {
return element;
}
问题 2:接口和类型之间有什么区别?
简而言之:
- 声明合并适用于接口,但不适用于类型。
- 不能
implements在包含联合类型的类中使用。- 不能
extends与使用联合类型的接口一起使用。
关于第一点,我所说的声明合并是什么意思?
让我来演示一下:
我在一个类中使用同一个接口时,定义了两次。该类会将两次定义中声明的属性都包含在内。
interface CatInterface {
name: string;
age: number;
}
interface CatInterface {
color: string;
}
const cat: CatInterface = {
name: "Tom",
age: 5,
color: "Black",
};
类型不会出现这种情况。如果我们尝试type多次定义同一个类型,TypeScript 会抛出错误。
type dog = {
name: string;
age: number;
};
type dog = { // Duplicate identifier 'dog'.ts(2300)
color: string;
};
const dog1: dog = {
name: "Tom",
age: 5,
color: "Black", //Object literal may only specify known properties, and 'color' does not exist in type 'dog'.ts(2353)
};
关于以下几点,让我们区分一下union类型intersection:
Union types允许我们指定一个值可以是多种类型之一。当一个变量可以存储多种类型时,这非常有用。
Intersection types允许我们将多种类型合并为一种类型。它是使用&运算符定义的。
type cat = {
name: string;
age: number;
};
type dog = {
name: string;
age: number;
breed: string;
};
联合类型:
type animal = cat | dog;
交叉路口类型:
type intersectionAnimal = cat & dog;
implements如果我们尝试将关键字与 `type` 一起使用union type,例如Animal`type=[]`,TypeScript 会抛出错误。这是因为 ` implementstype` 期望的是单个接口或类型,而不是联合类型。
class pet implements animal{
name: string;
age: number;
breed: string;
constructor(name: string, age: number, breed: string){
this.name = name;
this.age = age;
this.breed = breed;
}
}
TypeScript 允许我们使用“implements”:
a. 交叉类型
class pet2 implements intersectionAnimal {
name: string;
age: number;
color: string;
breed: string;
constructor(name: string, age: number, color: string, breed: string) {
this.name = name;
this.age = age;
this.color = color;
this.breed = breed;
}
}
b. 接口
interface CatInterface {
name: string;
age: number;
color: string;
}
class pet3 implements CatInterface {
name: string;
age: number;
color: string;
constructor(name: string, age: number, color: string) {
this.name = name;
this.age = age;
this.color = color;
}
}
c. 单一类型。
class pet4 implements cat {
name: string;
age: number;
color: string;
constructor(name: string, age: number, color: string) {
this.name = name;
this.age = age;
this.color = color;
}
}
当我们尝试使用extends联合类型时,也会出现同样的问题。TypeScript 会抛出错误,因为联合类型interface不能扩展类型。union type.以下是一个示例。
interface petUnionType extends animal {
name: string;
age: number;
breed: string;
}
你不能扩展联合类型,因为它代表多种可能的类型,而且不清楚应该继承哪种类型的属性。
但是你可以扩展 atype或 an interface。
interface petIntersectionType extends intersectionAnimal {
name: string;
age: number;
color: string;
breed: string;
}
interface petCatInterface extends CatInterface {
name: string;
age: number;
color: string;
}
此外,您还可以扩展单个类型。
interface petCatType extends cat {
name: string;
age: number;
color: string;
}
any问题 3: 、null、unknown和之间有什么区别never?
简答:
Any=> 这是一个顶级类型变量(也称为通用类型或通用超类型)。当我们any在变量中使用它时,该变量可以存储任何类型。它通常用于变量的具体类型未知或预期会发生变化的情况。然而,使用 `any` 并非最佳实践;建议改用泛型。
let anyVariable: any;
虽然any允许执行诸如调用方法之类的操作,但 TypeScript 编译器在此阶段不会捕获错误。例如:
anyVariable.trim();
anyVariable.length;
你可以给any变量赋任何值:
anyVariable = 5;
anyVariable = "Hello";
此外,你还可以将一个any变量赋值给另一个具有特定类型的变量:
let booleanVariable: boolean = anyVariable;
let numberVariable: number = anyVariable;
Unknown=> 这种类型(例如any`T`)可以存储任何值,也被认为是最高类型。当我们不知道变量类型,但稍后会为其赋值并在运行时保持不变时,我们会使用它。`T`Unknow是一种比 `T` 更严格的类型。any.
let unknownVariable: unknown;
直接对未知对象调用方法会导致编译时错误:
unknownVariable.trim();
unknownVariable.length;
在使用前,我们应该进行如下检查:
if (typeof unknownVariable === "string") {
unknownVariable.trim();
}
就像any,我们可以把任何类型的值赋给这个变量一样。
unknownVariable = 5;
unknownVariable = "Hello";
但是,我们不能将unknown类型赋值给另一种类型,而是赋值给其他any类型unknown。
let booleanVariable2: boolean = unknownVariable;
let numberVariable2: number = unknownVariable;
Null=> 该变量可以包含两种类型中的任何一种。这意味着该变量没有值。
let nullVariable: null;
nullVariable = null;
尝试将任何其他类型赋值给空变量都会导致错误:
nullVariable = "Hello";
Never=> 我们用它type来指定一个函数没有返回值。
function throwError(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while (true) {
console.log("Hello");
}
}
结束了……
最后我们讲解Typescript。
今天(?)
希望这对某些人有所帮助。
如果你有任何技术面试问题想让我解答,欢迎在评论区留言。🫶🏻
祝你一周愉快💖
文章来源:https://dev.to/giulianaolmos/technical-interview-questions-part-2-typescript-1njn











