TypeScript 面试题解析助你进阶 TS 专家

1,641 阅读9分钟

1. 什么是TypeScript?

Typescript 是一个强类型的 JavaScript 超集,支持ES6语法,支持面向对象编程的概念,如类、接口、继承、泛型等。Typescript并不直接在浏览器上运行,需要编译器编译成纯Javascript来运行。

2. 为什么需要 TypeScript?

  • TypeScript 快速、简单,易于学习
  • TypeScript 支持面向对象的编程特性,例如类、接口、继承、泛型等
  • TypeScript 在编译时提供错误检查功能
  • TypeScript 支持所有 JavaScript 库,因为它是 JavaScript 的超集
  • TypeScript 通过使用继承来支持可重用性
  • TypeScript 支持最新的 JavaScript 功能
  • TypeScript 支持静态类型、强类型、模块、可选参数等

3. TypeScript 的主要特点是什么?

  • 跨平台:TypeScript 编译器可以安装在任何操作系统上
  • ES6 特性:TypeScript 包含计划中的 ECMAScript 2015 (ES6) 的大部分特性
  • 面向对象的语言:TypeScript 提供所有标准的 OOP 功能,如类、接口和模块
  • 静态类型检查:TypeScript 使用静态类型并帮助在编译时进行类型检查
  • 可选的静态类型:TypeScript 允许可选的静态类型
  • DOM 操作:可以使用 TypeScript 来操作 DOM 以添加或删除网页元素

4. 使用TypeScript有哪些缺点?

  • TypeScript需要很长时间来编译代码
  • 它不支持抽象类
  • 如果我们在浏览器中运行TypeScript应用程序,则需要执行编译步骤才能将TypeScript转换为JavaScript
  • 网络开发人员使用JavaScript已有数十年了,TypeScript并没有带来任何新的东西
  • 要使用任何第三方库,定义文件是必须的
  • 类型定义文件的质量是一个问题

5. TypeScript 和 JavaScript 有什么区别?

image.png

6. TypeScript的组成部分是什么?

  • 语言:它由语法,关键字和类型注释组成
  • TypeScript编译器:编译器将用 TypeScript 编写的指令转换为等效的JavaScript
  • TypeScript语言服务:语言服务在核心编译器管道周围暴露了一个附加层

7. TypeScript支持的访问修饰符有哪些?

  • 公共(Public):类的所有成员,其子类以及该类的实例都可以访问
  • 私有(Private):只有类的成员可以访问它们
  • 受保护(Projected):该类及其子类的所有成员都可以访问它们,但是该类的实例无法访问

8. Typescript中的内置类型有哪些?

数字类型、字符串类型、布尔类型、Null类型、未定义类型、void类型

9. TypeScript 目前的稳定版本是什么?

当前的稳定版本是 4.2.3

10. Declare关键字有什么作用?

我们知道所有的 JavaScript 库/框架都没有 TypeScript 声明文件,但是我们希望在 TypeScript 文件中使用它们时不会出现编译错误,为此需要使用declare关键字。

11. any 类型的作用是什么?

为编程阶段还不清楚类型的变量指定一个类型,这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。

12. never和void的区别?

  • void 表示没有任何类型(可以被赋值为 null 和 undefined)。
  • never 表示一个不包含值的类型,即表示永远不存在的值。
  • 拥有 void 返回值类型的函数能正常运行。拥有 never 返回值类型的函数无法正常返回,无法终止,或会抛出异常。

13. any和unknown有什么区别?

unknown 和 any 的主要区别是 unknown 类型会更加严格:在对 unknown 类型的值执行大多数操作之前,我们必须进行某种形式的检查;而在对 any 类型的值执行操作之前,我们不必进行任何检查。

let foo: any = 123;
console.log(foo.msg); // 符合TS的语法
let a_value1: unknown = foo;   // OK
let a_value2: any = foo;      // OK
let a_value3: string = foo;   // OK

let bar: unknown = 222; // OK 
console.log(bar.msg); // Error
let k_value1: unknown = bar;   // OK
let K_value2: any = bar;      // OK
let K_value3: string = bar;   // Error

因为 bar 是一个未知类型(任何类型的数据都可以赋给 unknown 类型),所以不能确定是否有 msg 属性,不能通过TS语法检测;而 unknown 类型的值也不能将值赋给 any 和 unknown 之外的类型变量

14. tsconfig.json有什么作用?

tsconfig.json文件是JSON格式的文件,在tsconfig.json文件中,可以指定不同的选项来告诉编译器如何编译当前项目。

// 常用配置
{
  /*
      tsconfig.json是ts编译器的配置文件,ts可以根据它的信息来对待吗进行编译 可以再tsconfig中写注释
      include : 用来指定哪些文件需要被编译
      exclude : 用来指定哪些文件不需要被编译 :默认node_module
      extends : 用来指定继承的配置文件
      files   : 用来指定被编译的文件列表,只有编译少量文件才使用
      compilerOptions : 编译器的选项是配置文件中非常重要也是非常复杂的配置选项
  */
  "include":[
    // ** : 任意目录 , * : 任意文件
    "./src/**/*"
  ],
  "exclude": [
    "./src/hello/**/*"
  ],
  // "extends": "./configs/base",
  "files": [
    "1.ts",
    // "2.ts"
  ],
  "compilerOptions": {
    // 用来指定 ES 版本 ESNext : 最新版。 'ES3', 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ESNext'
    "target": "ES2020",
    // 指定要使用模块化的规范 : 'None', 'CommonJS', 'AMD', 'System', 'UMD', 'ES6'/'ES2015', 'ES2020' or 'ESNext'
    "module": "ESNext",
    // 用来指定项目中要使用的库 'ES5', 'ES6', 'ES2015', 'ES7', 'ES2016', 'ES2017', 'ES2018', 'ESNext', 'DOM', 'DOM.Iterable',
    //                          'WebWorker', 'ScriptHost', 'ES2015.Core', 'ES2015.Collection', 'ES2015.Generator', 'ES2015.Iterable', 
    //                          'ES2015.Promise', 'ES2015.Proxy', 'ES2015.Reflect', 'ES2015.Symbol', 'ES2015.Symbol.WellKnown', 
    //                          'ES2016.Array.Include', 'ES2017.object', 'ES2017.Intl', 'ES2017.SharedMemory', 'ES2017.String', 
    //                          'ES2017.TypedArrays', 'ES2018.Intl', 'ES2018.Promise', 'ES2018.RegExp', 'ESNext.AsyncIterable', 
    //                          'ESNext.Array', 'ESNext.Intl', 'ESNext.Symbol'
    // 运行在浏览器中不用设置,运行在node或其他中才需要设置
    // "lib":[],
    // 用来指定编译后文件的存放位置
    "outDir":"./dist",
    // 将代码合并为一个文件,设置之后所有的全局作用域中的代码会合并到同一个文件中 但是只能在  'amd' and 'system' 中才能使用
    // "outFile": "./dist/app.js",
    // 是否对js文件进行编译,默认false
    "allowJs": false,
    // 是否检查js代码是否符合语法规范,默认false
    "checkJs": false,
    // 是否移除注释,默认false
    "removeComments":false,
    // 是否不生成编译后文件,默认false
    "noEmit": false,
    // 当有错误时是否生成文件,默认false
    "noEmitOnError": false,
    // 是否生成sourceMap,默认false  这个文件里保存的,是转换后代码的位置,和对应的转换前的位置。有了它,出错的时候,通过断点工具可以直接显示原始代码,而不是转换后的代码。
    "sourceMap":false,

    // 所有的严格检查的总开关,默认false
    "strict": false,
    // 编译后的文件是否开启严格模式,默认false
    "alwaysStrict": false,
    // 不允许隐式的any,默认false(允许)
    "noImplicitAny": false,
    // 不允许隐式的this,默认false(允许)
    "noImplicitThis": false,
    // 是否严格的检查空值,默认false 检查有可能为null的地方
    "strictNullChecks": true,
    // 是否严格检查bind、call和apply的参数列表,默认false  检查是否有多余参数
    "strictBindCallApply":false,
    // 是否严格检查函数的类型,
    "strictFunctionTypes":false,
    // 是否严格检查属性是否初始化,默认false
    "strictPropertyInitialization":false,

    // 是否检查switch语句包含正确的break,默认false
    "noFallthroughCasesInSwitch":false,
    // 检查函数没有隐式的返回值,默认false
    "noImplicitReturns":false,
    // 是否检查检查未使用的局部变量,默认false
    "noUnusedLocals":false,
    // 是否检查未使用的参数,默认false
    "noUnusedParameters":false,

    // 是否检查不可达代码报错,默认false   true,忽略不可达代码 false,不可达代码将引起错误
    "allowUnreachableCode":false
  }
}

15. interface和type有什么区别?

相同点:

  1. 都可以描述 '对象' 或者 '函数'
  2. 都允许拓展(extends)

不同点:

  1. type 可以声明基本类型,联合类型,元组
  2. type 可以使用 typeof 获取实例的类型进行赋值
  3. 多个相同的 interface 声明可以自动合并

16. const和readonly的区别是什么?

  • const 用于变量,readonly 用于属性
  • const 在运行时检查,readonly 在编译时检查
  • 使用 const 变量保存的数组,可以使用 push,pop 等方法;但是如果使用 Readonly Array 声明的数组不能使用 push,pop 等方法

17. TypeScript 中的模块是什么?

TypeScript 中的模块是相关变量、函数、类和接口的集合,你可以将模块视为包含执行任务所需的一切的容器,可以导入模块以轻松地在项目之间共享代码。

module module_name{  
    class xyz{  
        export sum(x, y){  
            return x+y
        }
    }  
}

18.如何检查 null 和 undefined?

19. getter/setter 是什么?你如何使用它们?

Getter 和 setter 是特殊类型的方法,可帮助你根据程序的需要委派对私有变量的不同级别的访问。Getters 允许你引用一个值但不能编辑它。Setter 允许你更改变量的值,但不能查看其当前值。这些对于实现封装是必不可少的。

const fullNameMaxLength = 10;  
class Employee {  
  private _fullName: string = "";  
  get fullName(): string {  
    return this._fullName;  
  }  
  set fullName(newName: string) {  
    if (newName && newName.length > fullNameMaxLength) {  
      throw new Error("fullName has a max length of " + fullNameMaxLength);  
    }  
    this._fullName = newName;  
  }  
}  
let employee = new Employee();  
employee.fullName = "Bob Smith";  
if (employee.fullName) {  
  console.log(employee.fullName);  
}

20. 如何允许模块外定义的类可以访问?

你可以使用export关键字打开模块以供在模块外使用。

module Admin {  
  export class Employee {  
    constructor(name: string, email: string) { }  
  }  
  let alex = new Employee('alex''alex@gmail.com');  
}
let nick = new Admin.Employee('nick''nick@yahoo.com');

21. 简单介绍一下 TypeScript 模块的加载机制?

假设有一个导入语句 import { a } from "moduleA"

  1. 首先,编译器会尝试定位需要导入的模块文件,通过绝对或者相对的路径查找方式
  2. 如果上面的解析失败了,没有查找到对应的模块,编译器会尝试定位一个外部模块声明(.d.ts)
  3. 最后,如果编译器还是不能解析这个模块,则会抛出一个错误 error TS2307: Cannot find module 'moduleA'

22. Exclude、Omit、Merge的作用

  1. Exclude<T, U>:从 T 中排除出可分配给 U 的元素
  2. Omit<T, K>:忽略 T 中的某些属性
  3. Merge<O1, O2>:将两个对象的属性合并

23. TypeScript中的泛型是什么?

TypeScript 泛型是一种工具,它提供了一种创建可重用组件的方法,它能够创建可以处理多种数据类型而不是单一数据类型的组件。泛型允许创建泛型类、泛型函数、泛型方法和泛型接口。

在泛型中,类型参数写在开、闭括号之间,这使其成为强类型集合,泛型使用一种特殊的类型变量 T> 来表示类型。

function identity<T>(arg: T): T {      
    return arg;      
}      
let output1 = identity<string>("myString")    
let output2 = identity<number>( 100 )

24. TypeScript中的枚举是什么?

使用枚举我们可以定义一些带名字的常量。使用枚举可以清晰地表达意图或创建一组有区别的用例,它是相关值的集合,可以是数字或字符串值。

enum Gender {  
  Male,  
  Female  
  Other  
}

25. typescript 的类型推断机制

类型推断大概有两种情况:

(1)第一种在声明变量时赋值了,那么 typescript 会推断出该变量的类型,之后若赋值其他类型会报错。

(2)第二种声明变量时未赋值,那么 typescript 会推断该值为any,之后可以赋值为任何类型。