一、TS 有什么优点和缺点,适应场景是什么?
标准回答:
优点:
强类型检查:在开发过程中能提前发现类型错误,减少运行时错误,提高代码的可靠性。
更好的代码可读性和可维护性:明确的类型定义使代码更易于理解,也方便其他开发者接手和维护。
支持智能提示:开发工具可以根据类型信息提供更准确的代码补全和错误提示。
适合大型项目:有助于在复杂的项目结构中管理代码,提高开发效率。
缺点:
学习成本:对于不熟悉强类型语言的开发者来说,需要一定的学习时间来掌握 TypeScript 的语法和概念。
增加编译时间:由于需要进行类型检查和编译,可能会增加项目的构建时间。
适应场景:
大型企业级应用:对于复杂的业务逻辑和大型团队协作开发,TypeScript 的强类型检查和可维护性非常有价值。
前端框架开发:许多流行的前端框架如 Angular 采用 TypeScript 开发,如果你在这些框架下进行开发,使用 TypeScript 会更加得心应手。
需要长期维护的项目:可以提高代码的可维护性,减少后期维护成本。
白话回答:
优点呢,首先它有强类型检查,能在开发的时候就发现类型不对的地方,这样就少了运行时候的错误,代码更靠谱。然后呢,代码的可读性和可维护性更好,因为类型都定好了,别人一看就明白,也好维护。还有啊,开发工具能根据类型信息给更准的提示。对于大型项目来说,能更好地管理代码,开发效率高。 缺点就是得学,不熟悉强类型语言的得花时间学它的语法和概念。而且编译时间可能会变长,因为要做类型检查和编译。 适应场景就是那种大企业的大项目,还有用前端框架比如 Angular 开发的时候,还有那种要长期维护的项目,用 TypeScript 就挺好。
二、TS 的基础类型有哪些,数组和元组有什么区别?
标准回答:
TypeScript 的基础类型包括 boolean(布尔值)、number(数字)、string(字符串)、array(数组)、tuple(元组)、enum(枚举)、any、void、null、undefined、never 等。 数组和元组的区别: 数组:存储同一种类型元素的集合,可以使用类型注解来指定数组元素的类型,如 number [] 表示数字数组。数组的长度可以动态变化。 元组:固定长度的数组,且可以存储不同类型的元素。元组的类型注解需要明确指定每个元素的类型,如 [string, number] 表示一个包含字符串和数字的元组。
白话回答:
TypeScript 的基础类型有好多呢,像布尔值、数字、字符串、数组、元组、枚举啥的。 数组就是存一种类型元素的一堆东西,可以指定元素类型,长度能变。元组呢,是固定长度的,还能存不同类型的元素,得明确指定每个元素的类型。
三、TS 中 any、void、never、unknown 的区别是什么?
标准回答:
any:表示任意类型,可以赋予任何值,也可以对其进行任何操作,不进行类型检查。 void:通常用于表示没有任何返回值的函数。例如,一个只进行某些操作但不返回具体值的函数的返回类型可以是 void。 never:表示永远不会有返回值的类型。通常用于抛出异常或进入无限循环的函数。 unknown:表示未知的类型,与 any 类似,但更安全。unknown 类型的变量在进行操作之前必须进行类型断言或类型缩小,以确保类型安全。
白话回答:
any 就是啥类型都行,随便赋值随便操作,不检查类型。void 一般是那种没返回值的函数用。never 就是永远没返回值,像那种抛异常或者无限循环的函数。unknown 是不知道啥类型,跟 any 有点像,但是更安全,用的时候得进行类型断言或者缩小类型范围,保证安全。
四、TS 的访问修饰符有哪几个?
标准回答:TypeScript 中有三个访问修饰符:public、private 和 protected。
public:公共的,可以在任何地方访问。这是默认的访问修饰符,如果没有显式指定访问修饰符,成员就是 public 的。 private:私有的,只能在类内部访问。 protected:受保护的,可以在类内部和子类中访问。
白话回答:TypeScript 有三个访问修饰符。public 就是谁都能访问,默认就是 public 的。private 是只有类里面能访问。protected 是类里面和子类能访问。
五、TS 私有属性 #和 private 有什么区别?
标准回答:
#表示私有字段,是 TypeScript 3.8 引入的新语法。它提供了真正的私有属性,只能在包含它们的类中访问,不能在子类或外部访问。 private 关键字也用于表示私有属性,但它的私有性是通过命名约定来实现的,不是真正的语言层面的私有。在编译后的 JavaScript 代码中,仍然可以通过一些方式访问到 private 属性。
白话回答:那个 #表示的私有字段是新语法。它是真的私有,只能在这个类里面用,子类和外面都不能访问。private 也是说私有,但其实不是真私有,在编译后的 JavaScript 里还能想办法访问到。
六、TS 中 type 和 interface 有何区别,如何选择?
标准回答:
区别: type 可以使用联合类型、交叉类型等更复杂的类型定义方式。type 还可以通过 typeof 获取已有变量的类型。 interface 主要用于对象类型的定义,可以进行扩展(通过继承)。interface 还支持声明合并,可以在多个地方定义同一个接口,最终会合并为一个接口。 选择: 如果是定义对象类型,且需要进行扩展和声明合并,通常使用 interface。如果需要定义更复杂的类型,如联合类型、交叉类型等,或者获取已有变量的类型,使用 type 更方便。
白话回答:
区别就是 type 能弄联合类型、交叉类型这些复杂的类型定义,还能通过 typeof 拿到已有变量的类型。interface 主要是定义对象类型的,能扩展(继承),还能声明合并,在不同地方定义同一个接口能合并成一个。 选择的话,要是定义对象类型,还想扩展和声明合并,就用 interface。要是要定义复杂类型或者拿已有变量类型,就用 type。
七、type 和 interface 如何扩展属性?
标准回答:
interface 的扩展:通过继承来扩展属性。可以使用 extends 关键字让一个接口继承另一个接口,从而扩展其属性。例如:interface A {a: number}; interface B extends A { b: number }。 type 的扩展:可以使用交叉类型(&)来扩展类型。例如:type A = {a: number}; type B = A & { b: number }。
白话回答:
interface 扩展就是用继承,用 extends 关键字让一个接口继承另一个接口,这样就扩展了属性。比如 interface A 有个属性 a,interface B 继承 A 再加上自己的属性 b。 type 扩展用交叉类型,就是那个 &。比如 type A 有个属性 a,type B 就是 A 和另一个有属性 b 的类型交叉,就扩展了属性。
八、如何理解 TS 中的泛型?
标准回答:
泛型是 TypeScript 中一种使代码更加通用和可复用的机制。它允许在定义函数、类或接口时不指定具体的类型,而是在使用时传入具体的类型参数。这样可以使代码适用于多种不同的类型,提高代码的灵活性和可维护性。例如,函数可以使用泛型来接受不同类型的参数,并返回相应类型的结果。泛型还可以用于约束类型,确保传入的类型满足特定的条件。
白话回答:
TS 里的泛型就是让代码更通用、更好复用的办法。定义函数、类或者接口的时候不指定具体类型,用的时候再传具体类型参数。这样代码就能适用于好多不同类型,更灵活也好维护。比如函数用泛型就能接受不同类型的参数,返回相应类型的结果。泛型还能约束类型,保证传进来的类型满足特定条件。
九、TS 中交叉类型和联合类型是什么?
标准回答:
交叉类型:使用 “&” 符号表示。交叉类型是将多个类型合并为一个类型,这个新类型具有所有交叉类型的特性。例如,A & B 表示一个同时具有类型 A 和类型 B 特性的新类型。 联合类型:使用 “|” 符号表示。联合类型表示一个值可以是多种类型中的一种。例如,A | B 表示一个值可以是类型 A 或者类型 B。
白话回答:
交叉类型就是用 “&” 表示。把好几个类型合成一个类型,新类型有所有交叉类型的特点。比如 A & B 就是一个既有 A 的特点又有 B 的特点的新类型。 联合类型用 “|” 表示。就是说一个值可以是好几种类型里的一种。比如 A | B 就是这个值可以是 A 类型或者 B 类型。
十、TS 中有哪些特殊符号,分别是什么意思?
标准回答:
&:交叉类型符号,表示将多个类型合并为一个新类型。 |:联合类型符号,表示一个值可以是多种类型中的一种。 -?: 可选属性符号,表示该属性可以存在也可以不存在。 -!: 非空断言操作符,表示断言该值不为 null 或 undefined。
白话回答:
“&” 是交叉类型符号,把几个类型合成一个新类型。“|” 是联合类型符号,一个值可以是好几种类型里的一种。“?” 是可选属性符号,就是这个属性可有可无。“!” 是非空断言操作符,就是说断言这个值不是 null 或者 undefined。
十一、TS 常见的工具类型有哪些?
标准回答:
Partial<T>:将类型 T 的所有属性变为可选的。 Required<T>:将类型 T 的所有属性变为必需的。 Readonly<T>:将类型 T 的所有属性变为只读的。 Pick<T, K>:从类型 T 中选取一组属性 K 组成新的类型。 Record<K, T>:创建一个对象类型,其属性键为 K 类型,属性值为 T 类型。
白话回答:
Partial<T>就是把类型 T 的所有属性都变成可选的。Required<T>是把类型 T 的所有属性都变成必需的。Readonly<T>把类型 T 的所有属性变成只读的。Pick<T, K> 从类型 T 里选一组属性 K 组成新类型。Record<K, T > 创建一个对象类型,属性键是 K 类型,属性值是 T 类型。
十二、TS 如何扩展 window 属性,如何扩展对象属性?
标准回答:
扩展 window 属性:可以通过在全局范围内声明一个模块,然后在这个模块中使用 declare global 语句来扩展 window 的类型。例如:declare global {interface Window { customProperty: string} }。 扩展对象属性:可以使用 interface 或 type 来定义一个新的类型,然后通过交叉类型(&)将新类型与原对象类型合并,从而扩展对象的属性。例如:interface Obj {a: number}; interface ExtendedObj extends Obj { b: number }; let obj: ExtendedObj = { a: 1, b: 2 }。
白话回答:
扩展 window 属性呢,可以在全局声明个模块,然后用 declare global 语句来扩展 window 的类型。比如 declare global 里声明 interface Window 加上新属性。 扩展对象属性可以用 interface 或者 type 定义个新类型,然后用交叉类型把新类型和原对象类型合起来,这样就扩展了对象属性。比如先有个 Obj 接口,再定义个 ExtendedObj 接口继承 Obj 再加新属性,然后用这个新接口定义对象。
十三、TS 中如何定义第三方模块的类型?
标准回答:
可以通过创建一个声明文件(.d.ts 文件)来定义第三方模块的类型。在声明文件中,可以使用 declare module 语句来声明模块的名称和其包含的类型。例如,如果有一个名为 “my-module” 的第三方模块,可以创建一个 “my-module.d.ts” 文件,在其中使用 declare module 'my-module' { // 定义模块的类型 }。如果第三方模块遵循 CommonJS 或 ES6 模块规范,可以根据其导出的类型进行声明。如果模块有默认导出,可以使用 declare const moduleName: { default: Type } 的形式进行声明。
白话回答:
在 TS 里定义第三方模块的类型可以弄个声明文件(.d.ts 文件)。在这个文件里用 declare module 语句声明模块名字和里面的类型。比如有个叫 “my-module” 的第三方模块,就弄个 “my-module.d.ts” 文件,在里面用 declare module 'my-module' 然后定义模块类型。要是第三方第三方模块是 CommonJS 或者 ES6 规范的,可以根据它导出的类型声明。要是有默认导出,就用 declare const moduleName: { default: Type } 这种形式声明。